From a63f387e33c5917cc93e950cb9fe0981a6a7ad53 Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 13:38:56 +0100 Subject: [PATCH 01/11] Adding new application set with the use of commonLabels and CommonAnnotations Signed-off-by: Markos Kandylis --- charts/application-sets/Chart.yaml | 4 +- .../templates/_application_set.tpl | 27 ++++- .../templates/_git_matrix.tpl | 2 +- .../application-sets/templates/_helpers.tpl | 16 +++ .../templates/application-set.yaml | 97 +++++++++++++----- charts/application-sets/test-values.yaml | 30 ++++++ charts/application-sets/values.yaml | 31 ++++-- charts/fleet-secret/Chart.yaml | 2 +- .../packages/fleet-secret-0.2.0.tgz | Bin 28251 -> 0 bytes .../infra-tooling-fleet-secret-0.1.0.tgz | Bin 9026 -> 0 bytes .../infra-tooling-fleet-secret-0.2.0.tgz | Bin 14079 -> 0 bytes charts/fleet-secret/templates/_helpers.tpl | 13 +++ charts/fleet-secret/templates/ecrToken.yaml | 14 ++- .../templates/externalSecret.yaml | 2 +- .../templates/gitExternalSecret.yaml | 24 +++-- .../fleet-secret/templates/secretstore.yaml | 2 +- charts/fleet-secret/values.yaml | 27 ++++- 17 files changed, 230 insertions(+), 61 deletions(-) create mode 100644 charts/application-sets/test-values.yaml delete mode 100644 charts/fleet-secret/packages/fleet-secret-0.2.0.tgz delete mode 100644 charts/fleet-secret/packages/infra-tooling-fleet-secret-0.1.0.tgz delete mode 100644 charts/fleet-secret/packages/infra-tooling-fleet-secret-0.2.0.tgz diff --git a/charts/application-sets/Chart.yaml b/charts/application-sets/Chart.yaml index a4ef5d3..c4a117a 100644 --- a/charts/application-sets/Chart.yaml +++ b/charts/application-sets/Chart.yaml @@ -15,10 +15,10 @@ 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.2.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.0.0" \ No newline at end of file +appVersion: "1.0.0" diff --git a/charts/application-sets/templates/_application_set.tpl b/charts/application-sets/templates/_application_set.tpl index e4c29ff..9d4bfd8 100644 --- a/charts/application-sets/templates/_application_set.tpl +++ b/charts/application-sets/templates/_application_set.tpl @@ -25,9 +25,17 @@ Template to generate additional resources configuration {{- if $chartConfig.additionalResources.helm }} helm: releaseName: '{{`{{ .name }}`}}-{{ $chartConfig.additionalResources.helm.releaseName }}' + {{- if or $values.globalValuesObject $chartConfig.additionalResources.helm.valuesObject }} + {{/* Create a fresh copy for this component only */}} + {{- $chartValuesObject := dict }} + {{- if $values.globalValuesObject }} + {{- $chartValuesObject = deepCopy $values.globalValuesObject }} + {{- end }} {{- if $chartConfig.additionalResources.helm.valuesObject }} + {{- $chartValuesObject = mergeOverwrite $chartValuesObject $chartConfig.additionalResources.helm.valuesObject }} + {{- end }} valuesObject: - {{- $chartConfig.additionalResources.helm.valuesObject | toYaml | nindent 6 }} + {{- toYaml $chartValuesObject | nindent 12 }} {{- end }} ignoreMissingValueFiles: true valueFiles: @@ -39,7 +47,6 @@ Template to generate additional resources configuration {{- end }} {{- end }} - {{/* Define the values path for reusability */}} @@ -49,10 +56,22 @@ Define the values path for reusability {{- $valueFiles := .valueFiles -}} {{- $chartType := .chartType -}} {{- $values := .values -}} +{{- $valuesFileName := default "values.yaml" $chartConfig.valuesFileName -}} +{{- $applicationSetGroup := default "" $values.applicationSetGroup -}} + {{- with .valueFiles }} {{- range . }} -- $values/{{ $values.repoURLGitBasePath }}/{{ . }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}values.yaml{{ end }} -- $values/{{ $values.repoURLGitBasePath }}/{{ if $values.useValuesFilePrefix }}{{ $values.valuesFilePrefix }}{{ end }}{{ . }}/{{ $nameNormalize }}{{ if $chartType }}/{{ $chartType }}{{ end }}/{{ if $chartConfig.valuesFileName }}{{ $chartConfig.valuesFileName }}{{ else }}values.yaml{{ end }} +{{/* Path with applicationSetGroup if available */}} +{{- if ne $values.repoURLGitBasePath "" }} +- $values/{{$values.repoURLGitBasePath}} +{{- else}} +- $values{{$values.repoURLGitBasePath}} +{{- end }} +{{- if $values.useValuesFilePrefix -}}/{{$values.valuesFilePrefix}}{{- end -}}/{{.}} +{{- if $applicationSetGroup -}}/{{$applicationSetGroup}}{{- end -}}/{{$nameNormalize}} +{{- if $chartType -}}/{{$chartType}}{{- end -}} +{{- if $chartConfig.valuesFileName -}}/{{$chartConfig.valuesFileName}} +{{- else -}}/values.yaml{{- end -}} {{- end }} {{- end }} {{- end }} diff --git a/charts/application-sets/templates/_git_matrix.tpl b/charts/application-sets/templates/_git_matrix.tpl index 61353d3..7c3471a 100644 --- a/charts/application-sets/templates/_git_matrix.tpl +++ b/charts/application-sets/templates/_git_matrix.tpl @@ -21,7 +21,7 @@ generators: {{- if $chartConfig.selectorMatchLabels }} {{- toYaml $chartConfig.selectorMatchLabels | nindent 18 }} {{- end }} - {{- if and $chartConfig.selector $useSelectors }} + {{- if and $chartConfig.selector (eq $useSelectors "true") }} {{- toYaml $chartConfig.selector | nindent 16 }} {{- end }} values: diff --git a/charts/application-sets/templates/_helpers.tpl b/charts/application-sets/templates/_helpers.tpl index c705613..3fb0fdc 100644 --- a/charts/application-sets/templates/_helpers.tpl +++ b/charts/application-sets/templates/_helpers.tpl @@ -35,6 +35,9 @@ app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- if .Values.commonLabels }} +{{- toYaml .Values.commonLabels | nindent 0 }} +{{- end }} {{- end }} {{/* @@ -46,3 +49,16 @@ helm.sh/chart: {{ include "application-sets.chart" . }} {{ toYaml .Values.annotations }} {{- end }} {{- end }} + +{{/* +Merge common labels from global and chart-specific configurations +Usage: {{ $mergedLabels := include "application-sets.mergeCommonLabels" (dict "global" .Values.commonLabels "chart" $chartConfig.commonLabels) | fromYaml }} +*/}} +{{- define "application-sets.mergeCommon" -}} +{{- $global := .global | default dict }} +{{- $chart := .chart | default dict }} +{{- $merged := mergeOverwrite $global $chart }} +{{- if $merged }} +{{- toYaml $merged }} +{{- end }} +{{- end }} diff --git a/charts/application-sets/templates/application-set.yaml b/charts/application-sets/templates/application-set.yaml index b1191d6..8e8d289 100644 --- a/charts/application-sets/templates/application-set.yaml +++ b/charts/application-sets/templates/application-set.yaml @@ -1,8 +1,8 @@ {{/* First, import and merge all values files based on enabled components */}} {{- $values := .Values }} -{{- $releaseType := .Values.releaseType }} -{{- $useVersionSelectors := .Values.useVersionSelectors}} -{{- $useSelectors:= .Values.useSelectors -}} +{{- $releaseName := .Values.releaseName }} +{{- $useVersionSelectors := .Values.useVersionSelectors | toString }} +{{- $useSelectors := .Values.useSelectors | toString -}} {{- $globalSelectors := .Values.globalSelectors -}} {{- $chartType := .Values.chartType }} {{- $namespace := .Values.namespace }} @@ -13,9 +13,12 @@ {{- $repoURLGitRevision := .Values.repoURLGitRevision -}} {{- $repoURLGitBasePath := .Values.repoURLGitBasePath -}} {{- $valueFiles := .Values.valueFiles -}} +{{- $globalValuesObject := .Values.globalValuesObject | default dict -}} {{- $valuesFilePrefix := .Values.valuesFilePrefix -}} {{- $useValuesFilePrefix := (default false .Values.useValuesFilePrefix ) -}} {{- $argoProjectName := (default "default" .Values.argoProjectName ) -}} +{{- $applyNestedSelectors := default "false" .Values.applyNestedSelectors -}} +{{- $templatePatch := .Values.templatePatch -}} {{/* Merge values From Default */}} {{- if .Values.mergeValues -}} @@ -25,14 +28,15 @@ {{- end -}} {{- range $chartName, $chartConfig := .Values }} +{{- $mergedValuesObject := dict }} {{- if and (kindIs "map" $chartConfig) (hasKey $chartConfig "enabled") }} -{{- if eq (toString $chartConfig.enabled) "true" }} +{{- if eq ($chartConfig.enabled | toString) "true" }} {{- $nameNormalize := printf "%s" $chartName | replace "_" "-" | trunc 63 | trimSuffix "-" -}} apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: - {{- if $releaseType }} - name: '{{ $nameNormalize }}-{{$releaseType}}' + {{- if $releaseName }} + name: '{{ $nameNormalize }}-{{$releaseName}}' {{- else }} name: {{ $nameNormalize }} {{- end }} @@ -45,18 +49,44 @@ metadata: {{- if $chartConfig.labelsAppSet }}{{- toYaml $chartConfig.labelsAppSet | nindent 4 }}{{- end }} spec: goTemplate: true + {{- if $chartConfig.syncPolicyAppSet }} + syncPolicy: + {{- toYaml $chartConfig.syncPolicyAppSet | nindent 4 }} + {{- else }} + syncPolicy: + {{- toYaml $syncPolicyAppSet | nindent 4 }} + {{- end }} {{- if $chartConfig.goTemplateOptions }} goTemplateOptions: {{ toYaml $chartConfig.goTemplateOptions | nindent 2 }} {{- else }} goTemplateOptions: {{ default (list "missingkey=error") $goTemplateOptions }} {{- end }} - {{- if $chartConfig.syncPolicyAppSet }} - syncPolicy: - {{- toYaml $chartConfig.syncPolicyAppSet | nindent 4 }} + {{- if $chartConfig.ignoreApplicationDifferences }} + ignoreApplicationDifferences: + {{- toYaml $chartConfig.ignoreApplicationDifferences | nindent 2 }} + {{- end }} + {{- if $chartConfig.preservedFields }} + preservedFields: + {{- toYaml $chartConfig.preservedFields | nindent 4 }} + {{- end }} + {{- if $chartConfig.strategy }} + strategy: + {{- toYaml $chartConfig.strategy | nindent 4 }} + {{- end }} + {{- if $chartConfig.templatePatch }} + templatePatch: {{- $chartConfig.templatePatch | toYaml | indent 1 }} {{- else }} - syncPolicy: - {{- toYaml $syncPolicyAppSet | nindent 4 }} + {{- if $templatePatch }} + templatePatch: {{- $templatePatch | toYaml | indent 1 }} + {{- end }} + {{- end }} + {{- if $applyNestedSelectors }} + applyNestedSelectors: {{ $applyNestedSelectors }} + {{- else }} + {{- if $chartConfig.applyNestedSelectors }} + applyNestedSelectors: {{ $chartConfig.applyNestedSelectors }} + {{- end }} {{- end }} {{- if $chartConfig.gitMatrix }} {{ include "application-sets.git-matrix" (dict @@ -74,7 +104,7 @@ spec: - clusters: selector: matchLabels: - {{- if eq (toString $useVersionSelectors) "true" }} + {{- if eq $useVersionSelectors "true" }} {{- if $values.releases }} {{- range $releaseName, $release := $values.releases}} {{ $releaseName }}: {{ $release }} @@ -88,20 +118,20 @@ spec: {{- if $chartConfig.selectorMatchLabels }} {{- toYaml $chartConfig.selectorMatchLabels | nindent 18 }} {{- end }} - {{- if and $chartConfig.selector $useSelectors }} + {{- if and $chartConfig.selector (eq $useSelectors "true") }} {{- toYaml $chartConfig.selector | nindent 16 }} {{- end }} {{- if not $chartConfig.resourceGroup }} values: - addonChart: {{ $chartConfig.chartName | default $nameNormalize | quote }} + chart: {{ $chartConfig.chartName | default $nameNormalize | quote }} {{- if $chartConfig.defaultVersion }} - addonChartVersion: {{ $chartConfig.defaultVersion | quote }} + chartVersion: {{ $chartConfig.defaultVersion | quote }} {{- end }} {{- if $chartConfig.chartRepository }} - addonChartRepository: {{ $chartConfig.chartRepository | quote }} + chartRepository: {{ $chartConfig.chartRepository | quote }} {{- end }} {{- if $chartConfig.chartNamespace }} - addonChartRepositoryNamespace: {{ $chartConfig.chartNamespace | quote }} + chartRepositoryNamespace: {{ $chartConfig.chartNamespace | quote }} chart: {{ printf "%s/%s" $chartConfig.chartNamespace ($chartConfig.chartName | default $nameNormalize) | quote }} {{- else }} chart: {{ $chartConfig.chartName | default $nameNormalize | quote }} @@ -114,24 +144,33 @@ spec: matchLabels: {{- toYaml .selector | nindent 18 }} values: - addonChartVersion: {{ .chartVersion | default $chartConfig.defaultVersion | quote }} + chartVersion: {{ .chartVersion | default $chartConfig.defaultVersion | quote }} {{- end }} {{- end }} {{- end }} template: metadata: {{- if $chartConfig.appSetName }} - name: '{{$releaseType}}-{{ $chartConfig.appSetName }}-{{`{{ .values.addonChartVersion }}`}}' + name: '{{$releaseName}}-{{ $chartConfig.appSetName }}-{{`{{ .values.chartVersion }}`}}' {{- else }} - name: '{{$releaseType}}-{{`{{ .name | trunc 30 }}`}}-{{ $nameNormalize | trunc 20 }}-{{`{{ .values.addonChartVersion }}`}}' + name: '{{$releaseName}}-{{`{{ .name | trunc 30 }}`}}-{{ $nameNormalize | trunc 20 }}-{{`{{ .values.chartVersion }}`}}' {{- end }} + annotations: + {{- include "application-sets.annotations" $ | nindent 8 }} + {{- if $chartConfig.annotationsApp }}{{- toYaml $chartConfig.annotationsApp | nindent 8 }}{{- end }} + labels: + {{- include "application-sets.labels" $ | nindent 8 }} + {{- if $chartConfig.labelsApp }}{{- toYaml $chartConfig.labelsApp | nindent 8 }}{{- end }} + component: '{{ $nameNormalize }}' + environment: '{{`{{.metadata.labels.environment}}`}}' + cluster: '{{`{{.name}}`}}' spec: project: {{ $argoProjectName }} sources: - repoURL: {{ $repoURLGit | squote}} targetRevision: {{ $repoURLGitRevision | squote }} ref: values - {{- if eq (toString $chartConfig.enableAckPodIdentity) "true" }} + {{- if eq ($chartConfig.enableAckPodIdentity | toString) "true" }} {{ include "application-sets.pod-identity" (dict "chartName" ($chartConfig.chartName | default $nameNormalize) "valueFiles" $valueFiles @@ -142,17 +181,25 @@ spec: path: {{$chartConfig.path | squote }} targetRevision: {{ $repoURLGitRevision | squote }} {{- else }} - - repoURL: '{{`{{ .values.addonChartRepository }}`}}' + - repoURL: '{{`{{ .values.chartRepository }}`}}' chart: '{{`{{ .values.chart }}`}}' - targetRevision: '{{`{{.values.addonChartVersion }}`}}' + targetRevision: '{{`{{.values.chartVersion }}`}}' {{- end }} {{- if ne (default "" $chartConfig.type) "manifest" }} helm: - releaseName: {{ default "{{ .values.addonChart }}" $chartConfig.releaseName | squote }} + releaseName: {{ default "{{ .values.chart }}" $chartConfig.releaseName | squote }} ignoreMissingValueFiles: true + {{- if or $globalValuesObject $chartConfig.valuesObject }} + {{/* Create a fresh copy for this component only */}} + {{- $chartValuesObject := dict }} + {{- if $globalValuesObject }} + {{- $chartValuesObject = deepCopy $globalValuesObject }} + {{- end }} {{- if $chartConfig.valuesObject }} + {{- $chartValuesObject = mergeOverwrite $chartValuesObject $chartConfig.valuesObject }} + {{- end }} valuesObject: - {{- $chartConfig.valuesObject | toYaml | nindent 12 }} + {{- toYaml $chartValuesObject | nindent 12 }} {{- end }} {{- if $valueFiles }} valueFiles: diff --git a/charts/application-sets/test-values.yaml b/charts/application-sets/test-values.yaml new file mode 100644 index 0000000..54f2b1d --- /dev/null +++ b/charts/application-sets/test-values.yaml @@ -0,0 +1,30 @@ +# Test values for globalValuesObject functionality +globalValuesObject: + commonLabels: + app.kubernetes.io/part-of: "eks-fleet-management" + managed-by: "gitops-fleet-management" + team: "platform" + global-test: "true" + commonAnnotations: + managed-by: "gitops-fleet-management" + global-annotation: "test-value" + +# Enable merging from component files +mergeValues: + test: + use: true + +# Test components to verify globalValuesObject merging in the values/test.yaml +# Common configuration +syncPolicy: + automated: + selfHeal: true + allowEmpty: true + prune: true + +repoURLGit: "https://github.com/test/repo.git" +repoURLGitRevision: "main" +repoURLGitBasePath: "test-path" +valueFiles: + - "defaults" + - "environments/test" diff --git a/charts/application-sets/values.yaml b/charts/application-sets/values.yaml index 91c740b..1f1f5f3 100644 --- a/charts/application-sets/values.yaml +++ b/charts/application-sets/values.yaml @@ -1,3 +1,12 @@ +# Common labels to be applied to all ApplicationSet-generated Applications and resources +globalValuesObject: + commonLabels: + app.kubernetes.io/part-of: "eks-fleet-management" + managed-by: "eks-fleet-management" + team: "platform" + commonAnnotations: + managed-by: "eks-fleet-management" + syncPolicy: automated: selfHeal: false @@ -11,22 +20,22 @@ syncPolicy: maxDuration: 10m # the maximum amount of time allowed for the backoff strategy syncOptions: - CreateNamespace=true - - ServerSideApply=true # Big CRDs. + - ServerSideApply=true # Big CRDs. syncPolicyAppSet: preserveResourcesOnDeletion: true -repoURLGit: '{{.metadata.annotations.addons_repo_url}}' -repoURLGitRevision: '{{.metadata.annotations.addons_repo_revision}}' -repoURLGitBasePath: '{{.metadata.annotations.addons_repo_basepath}}' -useValuesFilePrefix: true -valuesFilePrefix: '{{.metadata.labels.tenant}}/' ackPodIdentity: path: "charts/pod-identity" # If we Define the Merge we will use the values files included in the folder values. That will enable us to have default helm charts -useSelectors: false -useVersionSelectors: true -# mergeValues: -# fleetBootstrap: -# use: true +useSelectors: "false" +useVersionSelectors: "true" # globalSelectors: # fleet_member: spoke # use_remote_argo: "true" +# Default Values files that will apply to all applciationSets +repoURLGit: "{{.metadata.annotations.addons_repo_url}}" +repoURLGitRevision: "{{.metadata.annotations.addons_repo_revision}}" +repoURLGitBasePath: "{{.metadata.annotations.addons_repo_basepath}}" +valueFiles: + - "{{if .metadata.labels.tenant}}{{.metadata.labels.tenant}}{{end}}/defaults" + - "{{if .metadata.labels.tenant}}{{.metadata.labels.tenant}}{{end}}/environments/{{.metadata.labels.environment}}/defaults" + - "{{if .metadata.labels.tenant}}{{.metadata.labels.tenant}}{{end}}/environments/{{.metadata.labels.environment}}/clusters/{{.name}}" diff --git a/charts/fleet-secret/Chart.yaml b/charts/fleet-secret/Chart.yaml index c9ce855..d0a9e3d 100644 --- a/charts/fleet-secret/Chart.yaml +++ b/charts/fleet-secret/Chart.yaml @@ -15,7 +15,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.2.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 diff --git a/charts/fleet-secret/packages/fleet-secret-0.2.0.tgz b/charts/fleet-secret/packages/fleet-secret-0.2.0.tgz deleted file mode 100644 index 7f283085abc374b3d4ea4ed32613aa54189e750a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28251 zcmV(?K-a$?iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMYaJe1$tIF1lwNtTkt-B6YoV_%Xbgt8OS!kD=+8Z%=S60#Pe z2#Hd5+Gv$Rp^{_`ZBj(`Qc3pZ_nO6;-t~Sy@8|RUzR&Zy|H#a_&$-UEU)Q<@a(ZqE0tx1W@W3$qlMfsYhpQ+lQUAi>@TGs@@~Wx~^2%^|MHMA^xC)#B zF0ZJpti%Ao|4}S{@sNom1c3qmFVD4o%PXiVEwO(kxV-#-?El~K&?Ik7 z!Vy3X2mpXsgfj+2X#g$=3=xC?z(F(?r2#;#=et2606;T!D8idaR~@3P2Lg+51qqb5 z1kjag;!x@r90sI*gn)-gAOVZO&|ob)+X8H?Ko>LGEHo7b56;ddOPobxara%zqDlk_ zULZjOfD%EB%lsfy8oIR3^ob~Mkr*#xG?s!Y1ONa)7LZp^R8m$^Ra2LT!%znaFc3+A z%}oG9cpwhrun2D=42koY9RXKYQ&mw`QdE%t?TG(r+xg${Eb{*(&;yS_kU*lGqZ^39 zg9IXsg#Xt=!EgM(qLMOviT_uTSNc!>_iuT8edVMf28ZwnED9jGfmGiB;9RJmsaYNj zA}#0d5ApSt1yG<18Vk<4HJZ-ACCQ2xt@t z_yHsW8H)r|6saH49@b(O(=o(3IO0?SG`9V@ zROfM=V+9Qe%!0Mrrs_l*hX z;eo@_&?No_O`1%59ED}RT{em+8DE6IjhCpU`QGUOTvca@}$puL73F90Ad%?y1-A&f`-yR zK>rB9Prl%PI!^z0J-^8RKqP^>u=q!60l&%r6qMoeOY=X4|J48fJeDT|qz`jXER)h|ey1 zDS6T#Kv}#{rZ7}iR(26nm*=E2NSbHlqQbzk92EZ}7RvhiQmFWumwz(+XJ)z(K%$#5 zmIM;K5EuG}003fL(O7U{y`0wWL4h|ro(g>K#k^=}4=rDq{|?aWDlp0) zJOSrU5jVYBM5~@d2_POvM3Zm?A1Hm;VvscBG}klM0H7oS8HCQgArmk(PR%JPoo4Dw zB1MU)bt+$9C?#!}Z2|oKq5l5!VDJbc(HloV&G(#bS=y71F(pr=G*a-~LBM%v(2F}n z9GQRwtw5J~!FLE*eVixeb`j5Q4%98l6 zs-&v?pZNdpclNp4_@j}ti>qZKA5@;|{+5ea-fce;sQ&xRFSBqK2H@f?^Ea>R?1KBqdd% zmm+4-rbD2iO?f>lfu*Suc!)$mAW8EKI0*Xu-Z%ZUtGSR$Bo0e2m|2qu2omV(qXFpC zu$}9J0+Bcr8tXdW!wiQ7=U&g6>a3TczWm>l4E-N`ev$wEwCnw^q=3KO|5H$2oc}4n z|Fi%1Z+e!5sh>|fEUf)dH-(nTf)?FQwYp>yj#_yCUs==pJI^ojKf3FuIM;uD3-EX6|5a2K zme&8^%8LKV|Nb4%PrLMULG@Rge!ou-7OYYi!aGP0<3FbhzZ!vEk~H86I4?8`B+$G9 z!h3EXg}#-q0nl?Ax;t3D6ti#!0uBStc?#N5J2HRv;4R1rmHT07`sW;HCZ<`7g46m>Y=kKvTooKLefi{LcQB z6_uCX|4~&|{g3_sJ03B>0zsk}G!Y=-0GjLpZ#NJNIFr#BO4J1K2;@P8D@cSv!~h#N zG!Y<@@pv48LEBA zKoU;?UC@U>6yS{}xd9ucV1PLm;{)KZ)R7dF06a(lFla0YgTVBy9jTTC5d-L_*KGBy z0Th}*giulqIqJVO^dT_k!vs0%zq1W)u5y%rX1@}>uyXT&oDs-_WIW)4#(+eKG>qtt zhe*Sm5eFgCFp>x5Gme0Eg-9QPhyk_;0vbmq0>=6VLYEzCO!fcSIsV%I<&~DP|Nq?o`8Pdczz&KA&;uv!b|3X5 zB?KZS2AF$M0xReZ`RU#t_0S~6J1jUzO%G}S8Veu*Lo~@8PXs`$7n*>>dVpAx4E5MF z+69e7U@$%a3M8Uku^@^Hj$W9by-PxK5eO`b4wE(=NPz;Q`5+o5ZU`(20}=rbGKPf4 zW5C(&l(jFBzLiKpau)o8iz_k<7SI3!i3Ev6s*eH;^sE5HqEi4eG#5p8dsKX#aU?g2 z_W_Zl**Rwm1ymrjNzxoo<_Q2uZUh|J)oredGMlsj=6KpM2N;$276gz0dXmvZGzla^ zWEV189wx7{1tLq6i6ZO}ME2Lf0QxEpL{a2IJ)=x#h9ng{fi8icFm@qhkra9m7&OU8 z3L*>W+F37TAbm@QJPTlUVtP(P=P4jb4JKqFMGO=OE+B$LCV)gLM1m_$Pyc5OX|<90 zBT0ZH$p=qY8wpxnL_&(nB)Zz#P~~k+6A*}^yb=TS=ycK%gUHfRBhJn{%ZNFOaf=lY zB0CF`xJcl?5imp+(4(`4l0eVN+iCI2)d|2604E~-Gwtdng(qYZ&I3WBQ;R^Mh`^kj z=y=l=j(+r*N~_hpF3*!|M7It@G-;8tFH-J>MAB*ufG!^YbEoA=`nkNgfRs77bLP1K zi&^cV@6uAKpqQTS(urq<_Oo3A)O&3Jfxb&iL6Ul^m`Hm;6Da*6!n|*pwf0|&Y1vai z%h83HmDMaJw9+Gu7>csf)svRF$O6_BMIqyX1sDC~=vumos{LL$~6yR2%3rGO5NRYBpSuEq< zUZ-88JTyXSIz+-ztd<7%r`y7_WX(nrT?=D+4?q@>pmdR-@{=NFj0_+_06lOdP-0O$ z{p%aYvx>A-cj!1SyjAv#Jm%*+%XFlvicP_UqS-VFfRrFY?>(F`ps?V zzfuO*#ZhQ!0R8`|LM*+6O{+Xn55QwZT5MU z`bncG^Uwk>{e&uA3V~%Ufwv z&53}1$!d}RqceZ`+x0);ut2&L9itGjj0gY=1Oedzl0X7Iu`)eTH$T9My3g$d`~-%Q15jWr zYo>mH(;|CbW;{hYl<{VZZn!Ohv0PfP3@n=JP@pYPq~%sU6qw5bz+tG!{1oXa@aRjW zxv|T}_y0!WEq~5;f&7+wt+}=C67l`^z$^vkBDejMKz`xxe>DLm7*l{+)50=k;SWes zBC{MV7)w!sQlt6uAQlAArOGlJZ5PV)&vqXdLZVZM#x83GOJx4D=lt&!`(Ix6S|I*q zmR?H*|5x{>mN6R&L<;Hizc%vMfaW~yEN5x{bruwz$5ex+`QhJ#TPUNSA_C4;AgvfV_4D+m}I zLU`aYAU&LIM-b7-xvbo3;p&2ZBxydw`0JI+f902Z=qm)8fBE@0gYX0#N|xRv3zz@v zpB$R)M;%1z{8veoHAo`k|90}UTt>8f?y@u|nblMxbn#+gL9uB`wdU`v6aGHy|0);% zO=kSt&+Jh?h%8{^wyYbaS3_q@ ze8j(~XZTz0W|!f3AKD zkp*_(2){1>%|Sz8F!R;Og|%SB+*BB!g@xheqaI|oazo=5Ky<^AF(~R!q+rpWWRO~K zr?YjApHyMY{*5heH*j_Upa6rzDEjk1y}9!T&!YXm z*_~cFG}eWHkR{=87&O*Zc3~e`Dt}=l*Ta84$6xRNDab3smz@7mkXMz5|7ZX2-|>h> zvhhbtBO3BGZkuR34tpJ=e#jth=@9O8wL1~9> z8wKeaMM_GFZItc3qHpAo9C#6aCE#VI)upDI@}pC7r+1pp>^d*JKUa8v)=4Y0>RQ*X zdl~0$HNAis&3q_+;VyK1htZZ^?F&`$cV{+oS!RT6G)`k4V0S*x9PW@i(I@Fe;@ibS zFsd}NF}+n+94PnLM(4>zRtp~SudnoVdBc=4nbp#RFQhsj<*jFT?_uZ37t#aSR+#f# zIloKVi+>l(jEBplDCYF*0GVJk@Iy{5Q2JBr^GoNFriuzR{BO+EJy)|2YhY&I#=@W@ z9Pu!7|JA%NE*bkujV;( zg`MBkLV$rGkWEcPz36TsnK*)vKi(;yDfrCj29`-aeftw$HD1FAFm&hE?R_8K*VMcy ze^Ytm*=oo3a&6q{(Ey>#O@iC?cJii$CJM#IC)&=Ox3D-m2aS0AyJ_8+Y0ZHzB?1@HRc|{#I{c%`0 zYerDwUQPIg72zw15zLK>l51kcrNaRB`b^d{jH<4~VwWK=B>CMmd5(t$hOv06f43}6 ze|;vDjf*k;oNij#b!PaE5T}bwM+uvAQK!~&9Mj=ed7I+m;>4Dw!yms<2*PySLN}!N zw3vO+b`vwk&`<`w-I83Lftwj}^`+|7rGvmQZzsvdwdEO%S;ua|oP$!BVt7K>%D9jLe9yuh!s=c5m1OE;|XdE%ps+bb+S@7iEJ1`~L2jk{5P z=gOQFy8>OK`*!V)&NwL`%;U*cA%VTR#yle|B|Q18zNZ6Ys8{^cle}k3+c)PvIB7ra zmg&O^1u;h*@4?$q<B>I-)1{0J?pBiG zNXw+&LnlMhGzVXFq)A0vmoDuxQDG2HxG6%X(xxhr`nV9$SvU zB)L&sMemHw72D~Rpmz}L-&%u_TP29tx zVp8xC)o~P)&w9f@E{?@yybFE3Czk+!b&8k*)qx&TV%}ik<7Zc|Ths#%X1eRhACOe! z6o}B@VUk~6DQVQd4s9cN{b?EZ3+FUR`BP~Rlh3?ZhpjbXVVBAhHPcgAQ>GbIWW~U6 zBIfzN8{H|uN{01v7EGc>P8Xv%nsx`ti!j9wr8zN(XQai1$%F|ig$gqzJwEN9^&?Pt zoIyuZs5}jEKiN0WxeZ-$tCTD^l>R~Jj9gF2iU)a6m+4E!&Prm~2M&*4R2>z!;p5-Q zhYF4nb>WOvtWAAzpa>zu7qf>eJz_-n>#+E^-}Yrx#3-R{@YA?9|S0ldJ)si5fL2Yb9dK)gr*qK%|cl}-tgl-yPym+h5+6bm6d+l90HjTR>7c` z14V)-UT)qgbf?6U;F@rw58m{KV8C+4C@y$6>L~T^3kfGDrv%mw7RkX1TgMF85GBVR5e7@vkKuSi^=Z5}ZcY||BcIT{Q-L*~cki4Ius`*-Jp`df(T<)>q z2AweF#+%?Ewi-bt7B?n*1FPrG@DNF^GZYn7Z=U<51q<*vD zZntn?Z}wgt0toM7V6HuhvS4yIV&W`M+XD=9pKy{jcH$UqJQy-@j7fc`71QPyG1mjs zhgjM#-pCA{y5gT2l>iU8?RaU=zVwy+N1r+?xu@#L8?k>Xw@G$zk~_KGnR(dRq1#lW?k$)vyPjrK>J48ow%_jDJES`m1d@(}1Qx%vW@dgT8xhZjX%BVev zx^{op!+UJZXTHPl^#Usls>c9szD>bZ@g zkTu+dRqFT;%hDVin@*G&BdK#A|CixCei>{Im4`$nGxCY<94sPAwis{c z%Wc|idE1zS&5Bqd{8;A8%Gj7tq>|*(h8W&(&UK$9y+}l!aJ3}0$PSkD<3Wr^1XgxN z?R%~GOjA+gR-5qwCKtgiN_V5uR+d?4q($m57at21Qiy3SeeTBSCecL{x{#BqxZXsG zUyPMCmQ&)ow*s>JJ(AUog%E~t$ZOE zc*eoweWikNSEs=WD-xdEg$_Jw>pQw_e*)ymP_c=`OGr%%jM4WRSIp!ESctgq<;3;_ zc&1J{Xpev7%jPX>;>xXc5_-1ZzS-dX@U)1!vmn_0BCcwmJ`iuoe?mxb-%#9F2lubx z279#~TrCBlnCtb|L$gv|F?gYo)r6Dk;<}d29q+H|{!speOyX(OI(6z4dpLy8hIBgK zzQFC3>YdUsf065vgnwzJz8_ber-;uxDqi?k_Eo~7<}3&fNPClFq;*T+g1*GEk?%{o_t(E ztI>uXDkkQGlph`yFfP~66foTT{>tO@aC`WrPknhoq?*vZm6|DszywASCV`mHyPOJJ z1d!|Oe9pf#@=@Eaz9F6ciM#q;mx81DD709_D;@0zD|v#DWsHU)6-(OJ4z^Pmdl z4*Bo+pflPVtIN4_i(c+uU(l1ARJKFpY&2it#GuRbGB@d9B`w7|+^b$zPoFKSr_Gor zH*FD{sZeYk{CrqZm@~7gM^G=6Wp!W>Tf{|x%UJ71U-W8AHkTna;~6e-M%+|%@%7fb z8+ycbV~tulwbvin#Vfw96KH!J! z2Gt!`j3phGedq8>-*2}_BIqP^&qFcDrtQm?vw`eSIO{E7GKw4l^+^%cyf8s;M9|IW zEedQQZRpD~&29%J8~Z+M32kqe()WzCb%GL!bz>>~zFBAPb3Vo)dO_*YX9K48@b#=v zy~62NJET`;e!uNdOPJoxalBjpV12S^o=|dHR5g^n%y@?=r*xR1eSEP&b@jetvr&_~ zuP#>vSo8^qe!GeK%4yZi8x~Q0h1}FHx=xet8LOp{60_E~@SP`gwUU46O4?*sJ=5c~ zTRVA5-2a&6*t5B%ZrVZ&ErSBglWwYp4Cw+e&30pa!0!ofp8t|;H!5hb3%?eB|Z>B@8Q@%vPQ5U zJBp~XG*8WK!M8BQ$JRR$h9#`sG%iQaj%J20_P9FH@nF5zELfl4n^Pk~L^yN7{2SY;S#A8Fs}nr;OG6F7T`|8FGp$ z??ZdXOMA6WYiYOdt4}Jcl6RaD&iQopqi%dQAb}?``Z(8YV zJy@~k$K+n^srEAk9ljL@?9RUQ9{cKA;_#AX!x?kqcL~{dQ_Tb}hzKT$PIh}A{joOX zTzo)Uz~i@eo3g-yD#A0YmT%G3;*X^tE2kvPMeL6FwT#Mkbfn;VI*;W3C>w3g+M7@T z<`tOvzb_$GJ;Rh7>@XkRq}}xT%=7#o&wUSlDd9dclx*pJ7%$fvRFTlS>P_S3SK4hs z`H!;Lxdt>_TOZ!RxAl#inMpKzZ@8IOkT{vwb?FS+=lwmd@>hoO(z%Z21Nm1wYu;`> znP5D53@pC#R*ir~$Ep6{=9KL191tnVFBU&iTX)pUyJfw-vBwvsz4cn9uMW89tBSRt zpPdNZb?)rmd^UfTRz~|C#iKjVw;uJo-($AtplSV~%&HF2gHd})4eI$X>IJs)=E;=! zdp9<8od3Mz^Sf8w!*94eJFeAL_ie+vSWSuWiRPkzNXv;mxpk;k4xm%ow(re;PrjDjo_pbT0Iu~*6+yl2kx39M=USwvARYI z7^~6dE*Q=0E*gv-t=Zdd`_jJT_|E(l&(}j74tx^{Q1`RBHBdgm?VsMA5> zF`j#A0y0?~Z~>q8qoE`2ytC`jzV>(BWUH3y&b98Xd2e-Z-Z$tdxHj%P97~$)(yO{* z6qmRE`?&{Rns4vPzDVnRtkfN74-<>NnsWQtK!;#d-se}=@H|}Z2i)4|ynaPwF!H@n z|9Bjvc1mONigj!C_M?1F)(y27P1_5aJsBy2j_Xho#mTqt<-)KvVP*$LjHdn6+s8WOuLii(NtrtDe^mr-319 zEWo+*)8XQo`_BgFwJTRyyZVv$J$O^+Zxi`Gh@-YT zTCL<&$FcD-F^lL$qd(#B^y2Ur=!nQF^IBuI7||oWfpvFUqOQI368-v?03Q^1H)u3HzDuqIZL;>H@5RgK zJ+C3&SdTPJZ*yo}-x{p(acH3Dx>&DhM8UfI@!l2Yu!&nZ`(*K+Gosv_NeTg|4qH_{ z520Mgh6mPtiPuIp)IH0~@q4==b(@ETD0-OO7Janlda+?0Mr22Jrq@?9gW>a6Q}jwQ?FCn|@q(7wetuKd}MrvtcFnl0b8wOM&kB4he~xzUTE~1@?oLJzS)@=-ODYt77w{%di?Nw+0w|Koz7qK-OG_~0_?s1+3J<`lnHu2qBj{C|f;#EL~ z7+x`WHNF4Z0gQb!)5IOk>~A589}I^BB%@j!lrknugJNSh%RTeGuab)5omh0{GK)KVEy%lGoJ$$D2^K~B$IJ$4u+uoT^QIDYB z)t$v|w{qaYPw$Gze?)&v7B0G0pmf~t&YRwWje=ini7%SAU5uB`*S>*%d?BA~b+g$d z-*K%b@$u1*A4N8rUoy6ij12K;&c@l?){Xe|;?{PD6yr&kPZ#_5ZpJuRaeTRdzhwYa z6h<90chD%mvj61+kuZN;~aa++n@g zTdt;kXuC(`_lnVa8_w=%iMX7PY6{7}zx#~J^u6Znx0kWGk%N&fVOLLHiHmC)?s^>N zvx{%-Dq`A!2w!0n(w4XVlV2a*so9mY?cSb-!jAIrPB9gArAd!HN1D+S;9I#?p3T#E z+^&&J<&Po@P-e|iU#4Evw%bPk;@%gP z(^`Ws8}beK1eCv+6vmzCy4U@5;v?sdM+&VZ&0*pd?YD;eKdbM&CqD4hVQ^x71cyR% zi*L$(r`v50i^jZLM@pny-nt)IQSu#O{vM;P>i#V?O1utqFUa@wzV2Uc8Tsmbp>%?@ z5dT%*nDLrwj*47s=A53lSt}u(@6661o!Yuor;Bor+s2RVY_|2wot!vWInWsqo0?dz ze&Xg4@|OF>58j_J?#`*$Sn@bPn{e?QJo}NI$n(hk#a}~|N8Xfh9nrX3EyNMOf8==4 z`2zR$lP=S`M#t}qzv`|JZ%aH;bWWsaCezJg-&eV<)6kO2IC7nbi`~mF?}g8m2vs_} zZdQMJC!z=DlUwLJ>Spg!MYehQ@t^_A+uC?#AH`3fN1x#ORD2X4?Eg@(a_iCCEogFI zIIh3u_@;ZliYm1`QwrMNWd=HZX~Ai0fG>wG%s8D4IB^kuv2Aip+rAgriS%7h zD0%&Zz4)AHqg1g}m$EF$p#IcrMdbKU>A+KXy_7+7$*Vo@`ow&TYK;!MjX~QqRD|hUXT9fm+ z)GA@K$FyhZQC8wqLwRY{LI0|gxe1X2!iO4Qb*=|qc{DH`);s2X;C=$*aqj7jHJPzI zC1+}OJo$VW$#-sB>QshUgy#y|%_=s*^2Bk;w=e7ipoJqnKWw+`RSkP(*Hzxx%4OuH z>iYR6hYHi${_+iLyju6PKI^Eh+f|!E?eSNJ*?uI!i z%ino|!31-OZ1HYUH)=lX882y zsZX_e+g{K7cqs8>uSrS?c{k&b_Jh@u^r9x#4Lz z!u)-W=J^NrC3EjKjz!91$uOA%H(GH+?OofpA9-D*g7{=HqJ3x6fce_e8&e!Ed1}7O zcgj{y*!HdHztz2epY}m_CM)0D`hp2W&Eil})77hEhkjs2@X>*RhTT^c8}nRqwUyJO zCt~b3dgD&`?Jo9sjkF`V4&Teyf~IVFbXsX}`rD&>8F>ddE6z;bY#Oh-@7tpClzS!l zpkuw__vD6tt->Fdrk)^thabR3y~a8wwq&!hny@#2SLirw*_LHA+GbS2Idd&nZiI-v zDwMA-vGs~~ZNFmJF6_M=N5T77KDur>H+)O~#s|+le{208e*IT_o4=8YbPZ13`*9$= zNcsJ{p_Y5C$08k2d0HDj-+?z|?Y@gawi^lt*QWA$4G59tl*!kX_GL&p1WtS*-!1u4 z`|zuV=z6k7uiDl_)~}H{mJwULuJvnqIJvYo!QSNceelh`!@f4>h_{=j$*29=k{HjN zq21aawbR?9PpUVjbwBA?3nTBc-$;6qJUaEkbmGG$2`{aF4ZoT^$*@2rgMQX^OdkgB z=06yI=Y1gs>rL+VFR&ZNS9Vqh)$%<{c;j1wDfC5-7(#fjAHzdlx+5j`?AUU0Z2AxG(*YFoFb_T(K@ zP2NP6NWlZI$6xYuKR&wIy17fKG+OJ(^D7@WYoXs_Ilvm%b`lpSKz(r;Rhej z<`SH*SA-gO*OT96+Lu^Lw0~~Cn{a75QKi_nWaFdSm!o98XPFcGw;#Vb-mR7EFOI?n zKJp10eSHEU6;SP?qSY}r{cr?1b9`M`ERqxn}J`ShlPGzJ8D?YTYJ>aSF`)t z=g6(6v#xI79oL+wV?4WwDBw|mKnY%Mo8B?@*x>(tj4gOS$7HNBoHgZ6!^ zc71nLvA%caHlMVidw#Kwf63+>*_-rrC*iRC|4=NiW~CL)u8BI*z0gJrKnwTO(pl${QUd(pJC-5n_3@M z;cHFYdpn@s`Fa0$r)M=&Z{8eLP7b(v`1<5~h|it>5d(mZ5Ids{N3dbCI>H3gLaur`(2yclRV`8J2#3 z{RNiYRY($&#Kj!>LdLg!ksW#Ux_LbRbMt|Y))wvWRaM>Zid3bNf20;CoZjs-cANtp zcJAe% zE6A%VsQl;r*T3WW7tVj(_tA`hH=%YiGs-P3_VI>iT2CIt%Z6UH)aO8UJ#nj!y60*% zEWUF~`UYd^Q)?_=#@gJm5wb>9VvdGY5my{Av;rxww$|QNhAkGgk;j19m~c4H2+BTif>d#H^8PU$fJb`g&ZZJJX6G)) z(BNW)$YO=2e7&1gd57tFkugZ|Z=!iZ9a zK$kwKkz9D5Ow84-ykR89i1-5i`lIem&?dPFn(Kt3l&+6?oMD@Wx)KLpEG7pxE4Qj_5BSx!e;|3+9WSNWME)G zG5W9=Yyt&#E0cYgh3}`Zt+do1{m6{Z+&Tz|gbf9^NfvKKGH>5+y4%3s*iv`8Ky3QN z+JKU&hcj_H-b`zZnKr7gwBy;u5W$nIKWe_c3^!Sl9;@J7IAf=C@!6K?{+XGGt`0E? z7DY83o%VW$N`cIAR)#MoQzkWoZEz`dZSBP9XqiCP^YUsjA6~jSFy(OaFrsn_fyzC+ z3^}qQk{4D4zOCnF-)YWa9%J-4Rkw64u)0!>KQ-NGD%`VR^VY3MZU*R0D8q&|;d<6u z4=+mgt=1K1XO4+eRExW@2Ui*r_1HOC9yl9oYjrB@5k^-C;~u~ZvO+fUv@xg0pxBs! z;;m2P3mS~Po&@euVg@3B!1B9DBR~lFz!4If-khqb_66>BF zQm?c*e8#7BhQ0Ady|}QvG^5bj0N4vDVF`QP6-))`-y&2Jg1T>q1*}$QS{)*$z5==) zc*7O*lylE*E0+_snK#=VRL#sCW4xaM>mN3@Sp;*D= zSQ-)~5u~UFUk{Nb8dPccG&0C5RSJn8cD7KgXjF4EHJq+Hq!wrMM*UlA=fUrTkzK59 z#KyofU2$=47PFkdH?O-IdXuk}5 zhE7IJnv0};#Z8Yy-(F{{F*3+&=D4@Pug<2dp5<|nzBs#iXj@ddbcToyxo*%%rc6L; zRffQ7&Y`Q4*T3mkmr2#by<>)uESkVBYppV?m^5{hHle?BS+vn$Fs|B z_e_0uaxgqQ5zi8Cl+3{vj;LRWVvv85ekN98iZQ;I1BB>-AebXh#+qpaWlHTZ+rn90 zse41)Iqhons-wr>6i>!|xRumv!o1NADa zcvK=i=di+s>QBZMiLnw^B355pc0MjTSRc8S=YolTkZtOB{=qmHs@a-h)V!s-smD=0 z`O<*^Q_-0(CY;kU)8<6A0CkR0k10ch%cPx!#klJ9jMxd0nN2#Ybb><*Hm{afJ99}* zW&gmCj85!;i?}Ol#mwna<+}DTGz$Z~Jh>Mnot;TFHTY&lw3aU#s{MLAPHpnJ(UW%f zlTRg}=Hvik`2+0?+=Xz}b)_=Jc)${+rRmD7rBSTSG3^_C)nn};eNa^eo@6Dc5 zWj(D~u%VjU_Zrvmi*!eMCOr%q_SfJpDKO5S(&+kl>_)$#yqYV+P-1JQ)Kt&8ka*0K z)A~>yAp*gqQ=srg*+z5zO=D?m*9y8dZmh4*E_N|;av5A90<1nI*}%VIIOKiFh{~;D zsiaH#A3TS66O|*v#e-n)g#xv>-d1Y4@f2bCY$tUKT`xM}}e&o;l& zdrn4qD2e8^I{KkOuN)=f*VSvAewZPTwK#eN#Lv_;ub4R~)%NAPc4yL9Yso~wjK#Ut zO)e>3PI*%nsz z_T`lZXh)~4c&=UB(rp!)q&~jy@aU^so;TD(U_}12Nyn_MzsxvqpAwa@E7`*F{GdDm zGcL^0Ot8U5#h_r=9#!OIv^8Q+XuZeSpEgZ5#unMH>lZ&3 zcSqB)?3w3|vS*&}gHLxv1;21C4Xl(&(s07_ghTmsk!yu7unB$G7|Xt?EXmS01bB7n zgGqS(1@%uk4<|Y9n%vuvJ#xFr_>2zxWy;y$;d(}6K+VZzwWc!D%2P3WWOf&Hudiw9 zv`bCwO=j;l@15$v_+TTaz_XfLb(=QDeQkO;G4ox$O|&y$NJqyrf2MLx+n3szD;K4! zTV|{+Ek;#mCM24#?jNsA)o9Ib{nEH1J4-~dcxni)wRL|Yjv2e7K?i8xChrzw^c`Y! zt5}UKcejL-D=b1DI_D}!ls;F&GqE0UiI%4~9HVitGTZeR&*gIhXuby>{tYoTK&IWI%=GGxX(KI;)#+v+V@>DT)9%^ z_1Sh9D}TH=(@-B)cAlHJ^Wuzx$lb!HmeUBoqne%s>%iogi&H43wy3gT9uo++&uS+t zyu`;NiTueQ&Bmf6ocB3dO~WlqS&bjJRq{Ap#W9q%#ordzKjnny6w-h8ypZo37JgSE z^kIhXy&1m2aL3mrr-SkyBr&#_%zUZ+c5vd1-C2*BXV0X=13u5BxTTlc9$@m+$$;wN;x?H7g4B}4V8|`@jhQ~h6;fIp9hKp5N zM4KX$!A({FXp04f>kNS+4A(2QJwr6f=AMJ))t{uWbAqZ#AV`hEQGK3jexKaATa z^?s%PDM6wFgvADk5=X~MSTi$-+d(gUYS*wgK#0o*vIKcmGn#eY-?UmhSOmoxx-p2y zBq8ZJB_T&A%9w6G{&fZX%~%lQ&~D-I?59T$??|fUWS2e!-OqFj?zOqYg8y*L9?r_W zyqh_CnjI7sV;%(QxiC1HnAD+rG+D$imW6U!KGq3pmX9IPVI!{Zc8s@~E6f{XVDfBq;Oj)?`@o$X+ zJe#W+*iHFHam<`?EJHn4)5PoJ2ECFW5Xj75^-EW$2W0QRlle8&%^YWSde^Sb_TzZe+`YCj zIVZ3youe{FU{&LYJnj8d5@IqTXXYo{qb!FH(mRC#p@KR%@L+dGfuHw+!aNmCSfD2hv{*B4{C6 zl_t+~n$4wfod{{*U`~7)|JIdXYZ>zS8K0W1KOw<%%V(^==rVsymq~YPQrT+VW^_J! z#GcXVI1glRPnCw8T?|_-AyAO5wB6F^xc1pXRt|W`b)a7OQ+zlnxB60W+9k41wX8_B(<%J&NRYu3+H+DO zsJ*eh_Zm)EksqBsQ16L)uv^7kL~){4VFUWIvo$=;O~sT6*c4vxYc9pRNwa#VXxH=X z)|O+_rxos+L~mDI(KBN%;M}zdVcC_&a$uYt;qb}nZhz&$4&vd1&)U!;XGJ77f2pcF&fS>(kS`}CDF^q; zlsmmM_x7dD20A4u<(ir2N!lZy$L-bN#%I!z{Ln5QMyCjI=sR6rqf80gV|$AEc(;DO z@uf+%bZvdg_^Gvhs?FTD<*MzxF3ShFOm}g7Kc5f+hg6&ll-uu2JSgy@#gEajPpd!- z&cV59Wol_+qlrIb1$tPs;?QxDWJgWTJN!9DWZrEj?mbE`(sYnknfGkB@h1|QN|BeI zRv#bYKQxrp@bR^J$HYwP*DkH@D)Ew}0|yDYTlM@(w!wD=sn>Ya@jlg@KJtKFvMiCQ z>Fk;WDUJZ0VBVzvkA1sbXu@$60IHxMAYDpINryOZ+3^uwq zke2RIV{|hZFlxkojQiz(-s5aJ|20V?&R`hbA^1xp=fp?Cu_!uxKJL%@i*1s(cXhiV z@}(%~Ncw@h?|yjLN@E>lwowFjyR6Bo&yi)Sj(jg}8Cb;1Hg|g6vf@EB3S7%?XEq{q_Rm-`Im4^GpWmo4y|uqr zQGwlO{nh`lp!IAI508K_1^qsPvn7%kN=xQPv) z#Ax~4ZKhwhwbLEc8-ud%@6-%0yBT}+Qe+^VCzzXL7f@Ci_F1_VQ0c5HJf=x2nBVbI z<=UpMiwq%+LT7IQH2ehs0GLrMUKSgnhani{eCfY-ua&VV*2fw9{-bo4;QQnH~7kuR1 zmtO0=NAx*^fAGa4 zo-Rtyxcgfm5Pka|*LTlqhcfoKSEw`a=us6Sj1V7yUOv!lW>UT#f8gQe1>d(nTxyH0 zo%DsykW;4#Onv6ye=;c<3&D>m(ntj%r&uTN@k2KR)m!R!F) ztfHapn)uNQ9*tJT^?u~ZE%4GjraAZGJn>2l&T2MswCCLya^>`+5a3Mh)^{p-_#Q*Z z^5W?pLzMdGSV*?J_MBv0Cru!JDwB@i=>B&5RCmgo*7~K9*BU}3XiduH+`uW~qPE4Y zv!d_umng&Tgx3lV#%)AQOD>#9NLYy!pASI(^5Xj{wjXnd%+tzV=sDC^mf@6B?RZV{ zLs>Y{|0hy-oGc@xNonVrlx)}mfI=M{K%N=4^+Wuz6PZ$FDYQ^&B-~R-oqhtJUbx02 zD|F%GQ2Mz2+V}Rea*+!+zpo=~&nLLc=y11yre!`rk3n>I@EKhO{etl-EPsgkHXZwD=5R+X{BbR!TEfTwF z0T+1vD}B7rn~Ow!6(CNEtr{7ycm7J@S7mt5@W5ZxE+ugdHZOqcnA--@$aT0+XC-Nq zmXu^hv0AN!H7@_D&|!@57kRfjCwng#ykykNs=!S_={M%MURUBU_&vL9!1TY=M_vj0 zqwBNO)cY4&#-JnIV2Bg{dwKNXo&x&aaKk*gD4pn+H-L2u{yfwWvkpJG*`@baKguil!ah)nbk>Sy#NP4mZjs##4eZajigvG;B2GyvfltG{gSE zrYGy&AV+gWK4zEocv`r%2k(hM0ceBoWYPN{twO$~;EKtf#MP4I4zY(DZ{4A&sgwK@ z>t7);JJq*MpHxXS@GQ0bejdtYdU}_viYE7cT5nkb3s8Nq>k+b?xU6xKKYWQ@-;5)< z)@0Q8)?Q3(w>sOVcwEaymQ10!+ijd5kMh~GeCjsV7XDfOYWKs_{V4Nh01MzhuT|S- zE+^l|Q=$@^YsWxsrpAuet2C=kL5X^Um5E|nExpqpx?dzi$>gWW-Sk9gVbtZE(0$lR z-N&D3hWhEMqC+i`HU-rS<6<}YV_HYykkip?ntM_}vbnC7vC%p**Laf|S| z(Hp!A3!mv!6!)JN`z_tO>e4+fVXg!jgpL1687#0{e~(6(E#me4UdhiP@fOkPsJyfv z-P^&$-~u%E#+OV>_LcT_mMr!l6!5+pSJ96mzV#WG>NG{|x8VXW01Ck4kP7x2ADW$f9NrX4qz zt_9?-erTPdmboKzDR44Ii$qrVhmJt}Ft)SMj)wOYMrddB%RT$QVM=+m%Pr1kmg>3r zJzs1CW|5FirZ?o7UwetUwh5YICTzCsmxt~CbPse_@bv6F9oOA2DM2oddC|g9ZA$fI z`f$?ZXl9uHC*p89_Z4Xc>2W+P+yy`l^i;9^>byIi?Jwhm``(8l3wQnU5-Gw$ao6Eo zm{7AB&kO9Bnk<#+4WrzJP+bwc@rjSJEoDUEOTg&X z{{fjan+qaZ9U2#{YcotZ*wsvUQuJuVB4r`Q6_Khwp3=rU`{MnqZli_%NZzTStARcn z{mk#KFSd;Sy}i|KB1{XaKIU^{x}h0!c<2hLwbA@{PvbxR*)}|Wux7(B?$JO$vn87A zE^S$|C<`oI8rgqIsL{$UJ2@fqoBE&{Y0$!D0t%!{{K#`k0A6H#m< zg^@WWrfIua3u3D9b`z%5|Ac8M_*|8*m^$mKlstvcd*+WV2hl>tUzwJy?1qXKz?&hETU5b5_nwN^o zfOC>xRyG;0r%#e4R?1f@8)@?~^{m(n@dK>e1Cr`~w#Ba1KGsJNE$NGKYBv2bT!P3r zp0kqrYO_=z;8GD-uASlO#=joufT*o@Rh7*CSlt<#?>YPg6wWGO)yU}cIOM5Rtd~4z zt~{mrSWc+_x{A(kmEf-=#*5V;>XsYzQhaK^$m}ySJOK4rfT1so_uH1;&bMDEV}6vb zJPxuBH;kXv!+THP*Mem4er~4SY+qmfxl;T#W3uQU1MVUuKmjxDgQxN_+~Sw`-;*le z@Yv>juekRsrpCY$N?iXiv37N5<7urrfusP55tW=lt$Vyx`B=`Dh46ylDX7}&72@*f z(@I?G?9>4ic>U;kMg$z=5_-Hfb^1fIfJ2MAmWY+?si4r7vDxmBa{21XJRP0D-!P4E z+2h15e{~~I^_5X;KND}+5xrM;cwhfB^&nBg!z(n#x5cbV!1a+LMfAH|0oh0skK}8i ztKv-e0Gt27Y68PsYPHN>iUL770!PxW9a)6ce|~v_=;i=~xKbA${)x8!Q`0 z9k=*4Q^=7;%@qyMW<-1Dm_Qq$w*MX1KV6_ciga>4qv~c&bdV3PTGRfwSkty>u80eF z`%Ah^n{6@J6mlsF2lz9FdwraHmpbv(*Z+jtQnH${Dm0!hLfY`p0m1Y_hYS8rK@>}J zNmvb@>;}y|iO~~cvsOM#!%+DaOJ};>>G>q!;wLCp<){nqTN>Nfn1G%?fFO>!dM2W! zOzEkE@v>ps_35p|<$@|?eHl_vFok0zL+R86b#cpi3&`Ktt}33B4@KPsT)!4*iU@jV z!p_F*yk%pR`5k1M%4QlCs54qu+P+Q<*T#ro@q?@8smk|ZKL z9eYe#&o?%Qo=OlH!j=uZej9%E!4QXmb+geSjKqPhqaFh7DGmD?we}XA$Go$$-zO!@ zcPkyFPsXyts(FbPm*&(Q^VuMln7TAYk^U9fT1g~k$LQNrBTl^Qx35Kd`<--rfMSP1 zz@~~o+NuV!v3;Ky-1#nTV9n3*Y9CG9Gq-AQcI(@2uuHM=Cpxo(j}Z%}t5#9d)Ab+r zwQg<99qchIVXs_dQACgL6dYzye(FN$!Qrktg6VgiiZ;<@Z26OO1dK zO`jfjq{0@o{A_r|C12Vn`t9@n_HVutLqa`R*nZEgj^eMkM4_)^5QrsVKgP;b1GxbD zsZ^VVu!K*XYoxMHKbnyfEBQ>jE!;|0!B1-G&NjudLL`0ybo5oH&F1;OCK^P<878sw z-cR0m&~(A#9wq?%u>;Cln`2*?8GB3Yh|Pcjo^i2p!9|-mLSqK{eQWbCE$wXA3$r4! zy`xa(iuc7+J?+*WN%D`B>xpZt<-(+Veng(D%`liB4;w14xR}o%EVn|@r3HI=lxa!* zu`FTGqWpGe)0EaP39Shi3_&wimKfS*I+8#>CUu#maw9HUuXBqXoVsJw4hg$*Wo4?;X>>tF@^icv4X?z7FS7xSg?IH8N$1L1)G+=x zI#$?YPexbdkyK+S4zOb-zCq;i#^`pw;;^BA#Lv3dmm`58+Zl?LG-Yclr7aKIXLFos z_`4t!L~q~izYWgHegSy@o@6+UYi*2v{*zYd-N|{EQDWNr{O^q|iO+uZMuC~+J!p4% z%;J7=l6YB2+So=2MVigRx4X(SYh5zak9+k@f|A>MBLAEbIvgCK4sd818A2IiId9|V zix^DqdDOeH9d#@7ZU>rHJS&i*SsmWkan$p+%Tg^{uM?FK!CwrBJ1U@TTf>tkG1dK$ z?@Ll~?{uzp1VGvVjj=SRKzmsp@vF+E5whAWrH{v$D_pf+JmfCa##)&P_0O+LGyLH| zy}JT0dp$Wj)>Gll3SY-ZzL^OjzNO#A_S#L?zrSqb%s)NOV&;#ESXPR#1{l_}pF69H zUR#!(iKF|X}dXo zz9#L#0Q^sqh!y64diZg8FQz-Ud+~C<+JvYl??qAqjijkhX(L2O^)@kghM*)&^fr`U z6UaNT@|Km;a6vg{sle1OB)q|HvwM%rEsuW1&Fuc}3$P^X9zBBqJH+G&ZM8w98OyHK zLQN^yC*NT+tF3Mbs?8}GJnP^6=sWJcT9l<)$U2ji54Nb++exEVufx!J*kuZ-%Y!NS z6+o%KKM*c;WZKFs#&|d-y?)B-C6~?rb)l~@(>`%ijw(`M#e*_iPo>;0J}&U+^C5jR zdrFee^1)e0TH#v6@!kO@hJsYpFy6MM+kg4F{O9fEw@Ho`dyq7&ZK;>Wd=_6Ji)mK~q zjduRavBuYesk>&}iQh~p7QF&K1S@Ejj>${I1imv=XIKhL8X%DsvC`EmNZc@6)C{o#Md%3} zi?IDX$NI;6)R);#WiFdx+slP7tr53ktNz{+HJxr3Qv+>f&W5O6kE7bsp9zkj!muX# z%8qDw`{shNpas31tp#hC{M)h6En2qwv+g2yE4DDOov`xLUP3u=BQoWgBMr6nT0>}^ zhg0)ra|aW3GesTtvYIi&-6N?l;tN)LFGSQNy+yX|nZ~HdrRyLEZD;rP5wK|&_4^JY zv#lA7Vg%u%P6Ka4gxxP?KBu@o=Zo{OxLG^uzg%SfHbp`u-P$fGDWMSd`D*@+n}?9q zMkCrPw7+$7W?a%vWQ8=jg#^W@tXd*v;#-FD5UfV}iY^A8Kl_<)0aos{WjDkHW_@mG z_9xjYaf-jr&Ws5Ro~>?t>;F*NfNM0m$Ax+9RTldFH0CDDwdkmk4XXvIae$HZeJSqQ zj7p(C@Sn$vz>Ax%#&yTFyMcgozL{x$Yv^(VyW#8c@dNiyXIqeJOYHH|jiP6W7>ZYT zZ=zS)Jdp@PTxN4qy!jD?Qaov$_;)~xYCbZ>DY??oX5}rZAC6XjG#}8G|69RYEKSKicGY{9-@rT>yYBhT!ml0r>t*`*%v$__f-$ z+SL3;EZJWG8c%XgW-G^UP>?`NSW1s?o`oSxx1}~&0F^hVO&|t|DY@? zX?V%!i(r*U@0sRsbui-}yxZ7(fu zd7r75>|U2EA@~Vo4iRxntzFUiaKYbRg-*eXR%;9rCD3203uY%!50G3iSlD7-%7U+s zn*F?kvwnEI50^y;&?lW-m4xIIIc$etl5L;BZr!cz!DYKX zWU~==Bj>FfSixFzsgaqwOHK&Qx5j6mjV`et**Anlgiq94pR>&EgMYuP7;pVC84&4M zR4uVJHZU70-zk?^nD_ed!H}$&6x?`<} zJ!#Nf5hi!}b9Sclq8}71BM^!Ww%oqPCpWixDkx>T^ol-?pHv(rX(NUPuZYbEM96lv zT1<7Tu6@z?I$rI6d`bh9S%Piw1IG^qUNG=dz5^$>p zD?7O~V=r`a2uq&4s`+8T((j8^1u-@Yg)F}huVE>FfoK3)ECqfpDrv^4=Qy{YOeTm+ z>so!HJ4wX_wyOiI0pEkN@{FIolUyp_ZzU;~GT1Z=$ZLOF1GzPHNdq3WNofXn*=i_W zZ&ajAHMsS7S53?vAmlo88PBa(LbWEV%OJbxNxVr~7BJ)64?(v^7m9n8Zt`p>`uzF2 zzW{yxB`2%3hL(ooJ90(8m596)NGc*lsP*XEbkx{Ddy-`vv{evsW%H1PE;4LH(7k8+ zV_7uuU$dfk-}i`1`<32VM6-SS6$b5PF8FXp0$As5bOwe zc5R%@hu{|8@p@rB3z;s%v;DI3#=vfUWD;u_3VqyuB){KwusU^Qd@BIH++~-{#~bx` zCTq8T{o@KzBmeUHi{L>=Hw;)1Y0rWOKC+)}rfZ|H;5j3-A)pTMsA@@wGO@?4@e7ve$Cs1~Mw?Uwb@2 zt2HF~Xv>|1=-EPjLsQuL0S1o}nl|T#z^TS635!N;nu{n35ji6N>Bwy3PLx6}gYSV7 zI$N@%jJkSzive%OA;sy{=-Hp8vQ;b3<}P0!27$<4#IWx@yWkww=0$H3ZT zaTdS(;3A((Y4c*9`KZ z&uA}}V?miGvH~!>91q%)^>|EiN9O)$_m<+W%-=SuN`{Nih z*~VHu-t_`BqxS}?LgE$TS*WlAN0OrGT;NCkCb`^~lwX*1msY;XhvZ>NSGxL3VH^q(gd z$a;3iEeUrZk{&37bcEO8@ur0RO<$LP2dV*)QPqAbz>*@=)3Tkz1l^Yr20pM0Iw7tO z?hc{zi+`JggKhWY;~!v}fyTm7>zB+}N--`~gnv@FaG;4xTJg2Bao@&vca4!QtzU`m zrRBZ&Ps$o2h}S~D7v75fs-x}uK!U{%<?R_A9mXTPL|<4GfDJPap>OjyF=Ly*n!I!pde@Zh|__obKqDv_@CbOaBUB z2o1_XD+SR;TIXwP5gwJ}~ zawjgT{-mYwf{banEpKhkcPhvNv1^hvk848MvX18uw(*=htmlBY3XZ*V`#+spDW?r1 z^WDL4@n=>e)PZ}Ob-J+qwMk*mOWn3@bIG6klf4GQ2IpuRpnx3!;yn4zBOKkuJ@L*CJ4W))lkGEO7PF~G zgA<%W!}c0u9fIDJn1ceCKVqzA5*XDf3_6n`Ey~u^2Qa zS!5vx^7}r-F>iM#BDDONr_A|UQ5-uM%gkmW#Bkd&r2ma$=#K%3!)HRXG_EH z%LPfw4O9|hl z449lc@e}S&p(8EH72etYTx<_#CeH3lT@%HjCDuc76DGmxBlw}*^E>nQ<46VfO& zcR#JLIutuI_@y0dbwv%Atw(}&n>e%YRrbu-Yp0$%Wd`?0(8#HMMm87oIG(Sxubnc1 zWHRaH;snrsqwnp4DG(tz?*h0=|IN&kOP+uKDRQ^=eYycWgx$4+Uu((iJU^C+OmBSuG zUeHXluz-5&A9yobcO%s^#J6&;4H6R@*PH*##2}sR;ZL2E)wsvwx;#;7%`e)K#bK+1 zlHQIh&2?oc@$ZN<@x@sQ<{F4Hm%Ae0QBaxwFmkdL!+fjcGQbTwAeT1gz7A>ikhp6z zHpe?iooUx^|Acdyu1MbpZY&i@LZj0)59iYPgh`XaL>aGr^_TU*`aR8JhU{WG|9+?2 zkuH^?N6we;gQ5JjQo`Q3*3s^YYYwieIxmmGyW|Q%^8*UykCmY6J*1^mNL8wZZ!M+# z{0BuXvIWn$_AyoCXF~>`U)22{VHdsjHya59&=pyLaLhe zCC)McZ*qa#yE1FeokdOl>XL`J@+%kKcG4I4|}G(Fhnh*Zt$9o`!#qF&K;MT56MV;dnFkUfv_((?ZOQvH$lwe zp}+E#it9^ge+eA@YECYY0q1~qm7E=PaTW-(z&K#)1A<)p^fq!4!g`#2HpSwLU0C)n z;}4x)=v_K6dJbgab%NDArV4nlf_9w&pnzXJm!m*nVsI$#UPqzsK4|UH+DvvrHoo-} zn7dh}TX7T=_{u!WQp7^QJIszRJl)7gKfNq(qVxmF+t^OiVv8_8i5DLEmW${=Q+9~j zna#3qaoD1tgdKX(R7iq1IcDO9-hDsoNdH7p;^T{du>R-}a2#%-CtzlW*#(0w)t#2l zQg^t8$e;Ho{>n;@ICXKXKf0)6bye4vG|g`n(}V@UW^mjkLw!Rhhs}HZ$IB6|GPAw5 z+FC}rF&*H5$EBt>DhB@?WJ4hn^2d=&1g6+Ov$or61Dfopt*!Fw8$Wd7+UiRNb4IgG zJJO?Yij&1X%^mFl;}Sboh3`Dp#4Q0L?hSui8oYOHPqP*~*1ku#v4TFF$%L=>_kDc zVQyr3R8em|NM&qo0PK8sTolLKICeda5+kui9S^a9SEvH9p`Z{Dq*$=QayQ4V?CqYr zdlUhCFGLX=HX>Gty*Cu2SOQ|lh&A?-sIiN{@3ZClQB3mn`+MKl`2+5DXP$Zb^UO0d zhvEtn5zz#Jp?I`Nfk8-%C@3Q#E$OR7NLrkLs4%O4)}&IY)Xl|({V$bDP5(=s99^v( zU8RmvX*U;V=DDMrv((iJNdMuy)oL1wMo25^Kc35!xc?yyQKN$)NudPp31XcDh+3`t z>L_+}6H5hhNXbZ4O|#FufG@;UK*p#BP!J^W*F-@Qhcu)FIHH1{pkjp*&Kpcs}pxhHE5R8HX0Af&F?g@mU`ffr20Gz1{kvNK17D}W-a3mU%%v%yhvqmjs zzYrK?KMG(x4M`lqI9L{EGr%SUDgwD#IHiO!+POrO77gR>{uWWCAQ=luPavcqrqB-> zz@f`^<|ks_$}kN@LsIud5RKA4Mu=0N3v*~PEl5lj2%wDg($GqX)4UH2BVr(K5hYGY zP)3SqrjOPOb1x`0B4eB}8BNIKhBAX1Q5>Xbk&_-xjvB73h=C^~p`5`0!6<$xMUx2; z5SOb76lYKs004jpI667IxVm+7_i&VoAC2mH2-es?-=lLrM~%gqRwVl$cikolsDP|95tAl^XkhCuujS%NPIu zIa*?(#9rVNuSRe=pp}sI4S-OvpV=%=EU=d(Ckql2ML-S}C=Ru5jnfh_Ge8JL`YwnD zqk))PJTRUEhzE0t1k*Dp7Lp_?had^iqz0D(H)r-EstVO86lgpUiiCiN1#vm^QNW_u zmxKrn0iv5hp~0{OFh+x5r~-v@Caq+V7Yl|!4mR7LW@carp+FRrAsPw-icmp=ypj>8iL!uy7m1x zMwxerS}l&znXMQlB)a8eVxom1NnnhIprHu|%%O0Zt6UV7zyZ=~QYa*2QJLv@j)S2* z^3~&^DXAihcyRezh5X{&fY>q(C{ED`E`!Dt7#f=;oi=s&6i+cClMx!6W;~#Vf>jd7 z4)Ws01Xkp%jJt_t90n#~qqAkw^|=&P)XfDWxAyLoyb@Jb}AX0051U(Oj|TpCAVS8!u1- zA4p)Rj0@CNfiWHelJucq03@TKkcTXX7&L?&0-3~>eWmq)9Qpu2JQ~HJVZEGtu43S6 z$Fsn7FZ80}8kaBVzXQImB4+-nNn$i3ZoXQ?)yst>R1*|R6J&yrA7%`aGtQvCex5)` zlNu=0z0r^u$0?nX@-(wADMpFdI#pt#kVzY~Pe5|AFgaNdMvYKZ96`$UJ+)6vd-51F zc_QfB8a17{76QMMT;?epzW-NC;3+TeTnJy;Tz`qi>Dp>p& ztN?&P%rP#bbN+*yit$pGsW30SYrUclhiZ+~F0L#@KA|J){w&45>d{w0#M1?^pg0_o z!iw-T34A=f>IO4yKfU8-TsKb(@WOF|W}#F1n1<4jWa&;!jAYoS6)Z?jj?{+-(^S(K zX+#fLAEl}}Z<5Hss~c9pDwzW$0h$O$R2WDCIEu?5P6J0N(9dn)X@VAO?QP8VDGjNV5jbB03#CbfhS3S0z?;KX*GCRzgdD}A^*sU!9O_R)eYs>-*{}3ha>!U<7g?iN+!{^oI%6fN9XVLZE!r$=G=^-0W)lu!{Li&Xusc z0{?yq=%oLjd!pZbPc1BX&TO^-v44k{kM~=s>(uqEhZYJ_1P3} z?Pg?0qx2vW1IfvJKMNJ43I-T~*6zC)jn*bg8n(Yta%YyY>M+#VazzEJNRxt9WQ7H* zNRxuqCtzS;mIfVAoPsh93H_pRf`q;VMf)=ej!Er}qLljK28eLaB4SvWpMZ zE5L<$SxK!$zm;ftH2YfLoif5x@8uK(DTCe+gESn1(n>-@hd>z-3&{jez{D2=FZJ)p zzY_ZwDbI0%}MC48@^XAodQ8U@b{t3;5G( zgZqX8IZ9FjCe@Ize>wC8;;6Btg#D|1pp2FxyySlnMezE`0(QH9KM&^9rT0hI=$Q8fm&-I=v7#c#DS zNNT|wTwLjDuz&`LOa>{6^-;j5ZwNq)P62e~Tomv2SbU=hTFH1HD5JG=Y6}G{5N(pA zBp*(vT8}3>obxc64(Ph&Y)zi{k~N zih%)s6$j;vT-YrxuaRDWH=Rk2kMy{Ly2U))2ie>&f-yt)o9m8!5YyavKuK z)fj-6T5aCRGD+s;MFUcFaCPQsfQ43j_&r^g3dZ!jOefX~&D<^ld#??U{GKj@Bzvlu z;$Cn9lxpLk`K0UiPskc7AlGAk8h z8CQ9o)<}6cLOC6x3C3zUxMsJ7wPa}{iPu0sz6TJ2PE40hEI%1BV_iWf5~>Iqb~1{m zmESnlDw3)0@HiUYDytxmF~76SBh4x{g9)SAoCF{fMEKr%MPkB(S^7|cHdS#Ox-w;m zHjYB30rdYgj{n4HK;dP2%>TkN~6V z5znA#looMwu|)a1Q%2fD6P#a#Mq~dCWn=_usEB&cWERFNQm1O%X4k8fnMN`5aDkUU zA47tP$WyM9tNgG00hP&V;1Qdc>GdHH`Zc&{|^e!@;P4v`I&kx-CEZ~ zd{rKpWndb)Z8L#X;O{G%fCTcGYfyvnTgC2E*N)VfwIxuG6;jg_%nLgFbzaf?=9B6D-k`JWVfrI)=7#BX8gWh(f}?oC-R8wMgny8hQjDhH(Vv|7$` z{#6T#=P_&0oFA?loS}@&A_>7sMjs3+heuhIyhY5+T)a;BOt{yo$A3`j6(8}kWbh19 z+7gF}%&T?O>mM0B6UqirNHhdK@rX)|K|Y-IK`2zF%gRFxR~P(|Bz=Za`O4+*{BjS! zLg4(1`ELfPNkT5-ABm)nmH*_BwjVo)>0DV76$)vM`je9n+{H8}(P}CsG+rzi z6q`({HJ`Fh_;l7+mWx-B8CRM7>ie3I3<3$7(K`|F!t~WXL|d-V7cKeXoW5uom7qP! zClG-!rA0T&S3|WWKB|&BE=_FT8d5g1~*y4n>$oMHg0NkR=tHtkSDQiW0! zd;5XKUst~sh(JGrthoHAgN9(3z8Yy*3r2KPVF`wXVariZpsn0++ya!6&|q@*CsJ^9 zj0UpxcAl*|ezL;Q{@-g_0d4^a9S=eBDt6!gK0o4I(3!!@F<<{2r|hic zLF0Bur=GssIk98tv(Vb}MGazRMOvLr$$L}s#}@VcTR;8cm$&14$6i;O)_3{vXTMt3 z1s#`9N!^yFxS&oIo*o#PAN=Qch4mMoo0NZ{f5h8YC8cGz%W}$YK6|^YyyU>-$^9o! z9#?*R-1sfy<;~kWj+ROMhGo2~+c%8i6eo0?m-2z@r+0a@rXg-V^@g?zo}PFQPA(0HWF z;~#5`TXfPnwbjrSI})xezkPSp(&L@&M(y3#=l0nRapSMg%<4UEW>Wng6CbT<^(rqT z#e9%l=wc6RhhVNGz#0T{7<7iHvr3&AT66(*IuV zua_>k8QyQRce5S)TAwRF(L~a?$(y&a^TafBDTn4!8KO=ezQf_|045R@Pa+ib}Nu(Hja{Uhy7(Ieb*J?#25KeYJT@ zUte_}XBsb1`mL$^3jDl(o^YY{(&LkzVrTSR;@#(UQryht7i!V9>BKbWfLSghkF1}xhVJ7SjIQn1Q!uV?t>JGMkFM)8 zV}fJe8teL#3eNpH{Hs&n%tD0|)Kk*VY&=%GLoJWYn!8UlEJ#_f=RuaFU>zXmtm>DW z-9Q~Mzy7a#mcZ1r0arc_-4onkh|2(E&cOOp*ITWgzvs&4`^LKV!P1@bid2fxBgdd`&+_;%| zw&coz1CHC?FNbc;hfZGiNd9~|;`!q-tbbKE{Er|p0OXzSURu6>}`P)32db2b^6`WkbvKp*enfe zyVZK{BKIB-PtHj=kRN{OfX}wvjIZ5xAMo39F!B}M@<{&rOE14^Oc#!OsH&ZDuVL9! z_tO47THhPrf;!~%?&z>1=^u%=8NHNu$4@T%vu|0PBNSM&9J;j&ukt>rlfZ8PDS>eJeV_T^4!O$+Hq zoU=VjzDauaRPKq^+oT6`28ZoT*mvr=n{%h{jz1mLaehQ9UB6d;O)I~57cV;G)b+4x zBkBC>jU8!u9S3fy722+5w?UobCnwG7(a)Ls~rN4TVEN;4I)3-~#tsHS=s{owmhcs zgEQer_J1gh+*&lF{J?>dk|r<8d$w&|zURd^n18I;qhQ^j@3McruBZ{TX<5S6lXY|a zZVlW1?!{oMTgR7=ZF&7&6X%70gHqAE!yFyyI?^biw)IF$_Dz!hcX~dqjbyBk0OtoJZJafc_$o-UCwy>e zM9QwINpVw(GH=!HmwN2^^>2#mW)`g#$M(VHo_}8pm`+Y~`*}va*yw3F9QzOp~-m;>0`l%-4 z7A-%vJ?cmu@#Vy2mwT?wBzL(5Zd`67dwG2B-tPrH)9zmzzc1inNs`;?@?nX$;{0nA z{aOA;TbGxIr5xoYJ#1keTkFE9D^#D{rH6BiG2pU@b7NMc>P+agYJN_cS$&+=@ZDv>2 z@7^4nzZ%J6XC^t;wu$I2d9Av8_-I*X%XL)N3Vc{A+o_{MMQHhD9?UE#L} zJB75|X?5y|Z|lv9CAoVuHqJ_)x8U8RfMD<3Z$CO^*7TnLV2*!{sA;I+LfTv;P~JWB zw^ttOu$I5r^$EP2AJZwkDdsgTbVbmS*fV4L^pe#a5_aa+;g$WKi~XA)aq1EBQ*95@ z7o8Sty<++Ji;J3J6MGzbbNk(QO;7t|gv@PIT9nbw`_9exDP>PuDK4~meInUr@o#~3 zlmWlb7EW(4Ah>p+aLmGbg{vL+)nDmDZtm$ExITABdBfeeYTZt=sX1!J@>w7R^~iIY zp`0W?K|gpJ;YD|eT6F8o_&#m79W5&i{4wyx@_s~MPHf7hH@&V9#pSuVw{J`?y?J6s zonH1!i;tlA+eZu1yteMD^GrM_@_2TJ=2=j?^spPb-}|a2w>sOX{zvb72S!*a7N_mN zQkEud{&B#C26O$=#lXvJeanummW%Ejc+xJ%|=wcHdav%Hz(@OU|6OpXER7)<`_X!#n-$GY|4(*g!A4S>RR69y97rAK#>p z?SexODQj`Py0JFXXQs~XnTajkwkel#nD}kp0^w|%9v#+_FI?*1ez=EPH8HlV{Ib;g&%JxxHpmVKb!%VHGxMC%>pr^msr|-b zd-lFA_;&f@`zxjh222Us^D^UlQQ)OW_~1y#4F9jKFZXk9tQxgBCp)Wm&g(OWOK!QR zUl_fs;c>2gQ$+QA>#XV8@pQXEMUlDnUWmIj-re-vx!;D1T4b(19qqO- z(xp|nY-8d_7=q5A6L#(zbuTq_uryqwDO!O0?{&Dp@6N*TIv*Q#I_Wq&cvIM8r%{P3 zcCYI`ZD3~K)Gpbs>fw8)wh0%fes^d*Ozd4hC~R@3^i@CPKceD~jycnz=f>j~E_F*; zA#~N8`2FEc`{Ai4c0TJme9f*Xhk!Ii>joJD;qJJ#zV_XBo}X@ab?1GzSN3qp<;;Q3 z6Uvs^d%tYDdt>)Ucbxj(Oe^lED7^S?;+_@Vlnv^^9;HJLA-{LHGNbtL%F+?pZNuvh z*x0CGMEZuJUNh3)_$ofyU(OrX5Qf!!ZL{sKfKAezfE8|gCyd${=AU`7d&lQjzd715 z);(_IjaPrZx;$E4(6`tnY9-zMfXz>hwD(W~Sp zK55yo#=i?r*zVod^vPv=72ZvHzT}#%a$>N@gh}Vnb={;{zfmXR?b^7Oss5NYyjH&< z!_LbPYrX_V!h?++TBBmy;Z#zEY;^KBM9y`TPO>-FI zBF{M&yk(11yv^xxHu72O4@>i3HdDEmv=b^vDDFj}Xl!>!8N_#*Ll#_@Mnv&;H?e;{sRicUt}DwY5tZxDT*7^dTzO!Obll7=)W5wr ze0}R(mj({`{TqkJdsA~;w13cI(d+YI%FX@R*@ZQnf3kY8{N72;)%muoLfgOkqu$){ z;fKHK^KSl&d-o1EZRK|%tJR~TNq!!K_CIg<%k^N1dSUBdMApuO*UT)NnKoyf{lKDP z(QyB9qmjKgW49g`*?oIxc#U?C4|ixW+CIG(eAxWl^yfFrT?&w!i_Qi}#3044~{oB#j- diff --git a/charts/fleet-secret/packages/infra-tooling-fleet-secret-0.2.0.tgz b/charts/fleet-secret/packages/infra-tooling-fleet-secret-0.2.0.tgz deleted file mode 100644 index 9b8b3d7150a77f7ccffcfa189a02e56eb96faf73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14079 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 Date: Fri, 3 Oct 2025 13:42:27 +0100 Subject: [PATCH 02/11] Addign globals in versions for generic Signed-off-by: Markos Kandylis --- charts/application-sets/examples/addons.yaml | 174 +++++++++++------- .../application-sets/examples/monitoring.yaml | 172 ++++++++++------- .../examples/versions/applicationSets.yaml | 41 ++--- 3 files changed, 221 insertions(+), 166 deletions(-) diff --git a/charts/application-sets/examples/addons.yaml b/charts/application-sets/examples/addons.yaml index 3e0bcd2..ab6eec3 100644 --- a/charts/application-sets/examples/addons.yaml +++ b/charts/application-sets/examples/addons.yaml @@ -10,71 +10,71 @@ spec: goTemplateOptions: - missingkey=error generators: - - matrix: - generators: - - matrix: - generators: - - clusters: - selector: - matchLabels: - fleet_member: hub-cluster - values: - chartName: application-sets - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" - chartPath: "charts/application-sets" - applicationSetGroup: "addons" - groupRelease: '{{default "" (index .metadata.labels "addonsRelease")}}' - useSelectors: "false" - useVersionSelectors: "true" - - git: - repoURL: '{{ .metadata.annotations.fleet_repo_url }}' - revision: '{{ .metadata.annotations.fleet_repo_revision }}' - files: - - path: "{{ .metadata.annotations.fleet_repo_basepath }}/bootstrap/versions/applicationSets.yaml" - - list: - elementsYaml: | - {{- $releaseTypes := index .releases .values.applicationSetGroup | toJson | fromJson -}} - {{- $result := list -}} - {{- $defaultVersion := dict -}} - {{- /* Defining the Default Version in case we need to fall back */ -}} - {{- range $releaseType := $releaseTypes -}} - {{- if eq $releaseType.type "default" -}} - {{- $defaultVersion = $releaseType -}} - {{- end -}} + - matrix: + generators: + - matrix: + generators: + - clusters: + selector: + matchLabels: + fleet_member: hub-cluster + values: + chartName: application-sets + chartRepo: "1234567890.dkr.ecr.eu-west-2.amazonaws.com" + chartPath: "charts/application-sets" + applicationSetGroup: "addons" + groupRelease: '{{default "" (index .metadata.labels "addonsRelease")}}' + useSelectors: "false" + useVersionSelectors: "true" + - git: + repoURL: "{{ .metadata.annotations.fleet_repo_url }}" + revision: "{{ .metadata.annotations.fleet_repo_revision }}" + files: + - path: "{{ .metadata.annotations.fleet_repo_basepath }}/bootstrap/versions/applicationSets.yaml" + - list: + elementsYaml: | + {{- $globals := .releases.globals -}} + {{- $releaseNames := index .releases .values.applicationSetGroup | toJson | fromJson -}} + {{- $groupRelease := .values.groupRelease}} + {{- $firstRelease := index $releaseNames 0 -}} + {{- $result := list -}} + {{- /* If the values of the group release is empty or we dont use version selectors then we use only the first element of the list */ -}} + {{- if or (eq $groupRelease "") (eq .values.useVersionSelectors "false") -}} + {{- $mergedRelease := merge $firstRelease $globals -}} + {{- $mergedRelease = merge $mergedRelease (dict "totalReleases" (len $releaseNames)) -}} + {{- $result = append $result $mergedRelease -}} + {{- else -}} + {{- /* We look for the defined releases */ -}} + {{- $found := false -}} + {{- range $releaseName := $releaseNames -}} + {{- if eq $releaseName.releaseName $groupRelease -}} + {{- $found = true -}} {{- end -}} - {{- /* We look for the defined releases */ -}} - {{- range $releaseType := $releaseTypes -}} - {{- /* Case 1: If selectors is true, include all group releases */ -}} - {{- if eq $.values.useSelectors "true" -}} - {{- $result = append $result $releaseType -}} - {{- /* Case 2: If group version Release value exists, only include matching releases */ -}} - {{- else if $.values.groupRelease -}} - {{- if or (not $releaseType.type) (eq $releaseType.type $.values.groupRelease) -}} - {{- $result = append $result $releaseType -}} - {{- end -}} - {{- /* Case 3: Default case - include version if it's the default type */ -}} - {{- else -}} - {{- if eq $releaseType.type "default" -}} - {{- $result = append $result $releaseType -}} - {{- end -}} - {{- end -}} + {{- end -}} + {{- if $found -}} + {{- range $releaseName := $releaseNames -}} + {{- $mergedReleaseValues := merge $releaseName $globals -}} + {{- $mergedReleaseValues = merge $mergedReleaseValues (dict "totalReleases" (len $releaseNames)) -}} + {{- $result = append $result $mergedReleaseValues -}} {{- end -}} - {{- /* If no releases were selected, use default */ -}} - {{- if eq (len $result) 0 -}} - {{- $result = append $result $defaultVersion -}} - {{- end -}} - {{ $result | toJson }} + {{- else -}} + {{- $mergedRelease := merge $firstRelease $globals -}} + {{- $mergedRelease = merge $mergedRelease (dict "totalReleases" (len $releaseNames)) -}} + {{- $result = append $result $mergedRelease -}} + {{- end -}} + {{- end -}} + {{ $result | toJson }} ################################################### #base template (everything common) ################################################### template: metadata: - name: 'cluster-{{.values.applicationSetGroup}}-{{.name}}-{{.type | lower }}' + name: "cluster-{{.values.applicationSetGroup}}-{{.name}}-{{.releaseName | lower }}" spec: project: default destination: namespace: argocd - name: '{{ .name }}' + name: "{{ .name }}" # syncPolicy is identical for both variants syncPolicy: automated: @@ -90,46 +90,78 @@ spec: # conditional sources ################################################### templatePatch: | + {{- $commonValuesPath := printf "%s/%s.yaml" .values.chartName .values.applicationSetGroup -}} + {{- $repoNames := list "addons" -}} + + {{- $environment := .metadata.labels.environment -}} + + {{- $tenantPath := "" -}} + {{- if and (hasKey . "tenant") .tenant -}} + {{- $tenantPath = printf "%s" .tenant -}} + {{- else if (index .metadata.labels "tenant") -}} + {{- $tenantPath = printf "%s" .metadata.labels.tenant -}} + {{- end -}} + + + {{- $clusterName := "" -}} + {{- if and (hasKey . "clusterName") .clusterName -}} + {{- $clusterName = .clusterName -}} + {{- else -}} + {{- $clusterName = .name -}} + {{- end -}} + + {{- $pathPatterns := list + (printf "%s/defaults" $tenantPath) + (printf "%s/environments/%s/defaults" $tenantPath $environment) + (printf "%s/environments/%s/clusters/%s" $tenantPath $environment $clusterName) + -}} + spec: sources: - - ref: values - repoURL: '{{ .metadata.annotations.addons_repo_url }}' - targetRevision: '{{ .metadata.annotations.addons_repo_revision }}' + {{- range $repoName := $repoNames }} + - repoURL: '{{default (index $.metadata.annotations (printf "%s_repo_url" $repoName)) (index $ "repoUrl")}}' + targetRevision: '{{default (index $.metadata.annotations (printf "%s_repo_revision" $repoName)) (index $ "targetRevision")}}' + ref: {{$repoName}}Values + {{- end }} {{- if eq .use_helm_repo_path "false" }} - repoURL: '{{default .values.chartRepo .chartRepo }}' chart: '{{ default .values.chartName .ecrChartName }}' targetRevision: '{{.version}}' {{- else }} - - repoURL: '{{ .metadata.annotations.addons_repo_url }}' - path: '{{ .values.chartPath }}' - targetRevision: '{{ .metadata.annotations.addons_repo_revision }}' + - repoURL: '{{default (index .metadata.annotations "chartRepoUrl") (index . "chartRepoUrl") }}' + path: '{{ default .values.chartPath (index . "chartRepoPath")}}' + targetRevision: '{{default (index .metadata.annotations "chartRepoRevision") (index . "chartRepoRevision") }}' {{- end }} helm: ignoreMissingValueFiles: true valuesObject: - useSelectors: false + useSelectors: '{{.values.useSelectors}}' useVersionSelectors: '{{.values.useVersionSelectors}}' + applicationSetGroup: {{.values.applicationSetGroup}} + groupRelease: '{{.values.groupRelease}}' # Defining the way to group addons This application set will handly Addons and ACK values mergeValues: addons: use: true ack: use: true - releaseType: '{{.type | lower }}' + releaseName: '{{default "" .releaseName | lower }}' # If we are using version selector we add the version of the releases on the matchlabels - {{- if eq .values.useVersionSelectors "true"}} + {{- if and (eq .values.useVersionSelectors "true") (or (eq .releaseName .values.groupRelease) (gt (int .totalReleases) 1)) }} releases: - {{.values.applicationSetGroup}}Release: '{{.type | lower}}' + {{.values.applicationSetGroup}}Release: '{{.releaseName | lower}}' {{- end }} {{- if eq .values.useSelectors "false"}} globalSelectors: fleet_member: hub-cluster {{- end }} - valueFiles: - - defaults/{{.values.applicationSetGroup}} - - clusters/{{`{{ .nameNormalized }}`}}/{{.values.applicationSetGroup}} + # Those are the Value files to read for the Whole group of applciationsdts valueFiles: - - $values/{{ .metadata.annotations.addons_repo_basepath }}/bootstrap/defaults/{{.values.applicationSetGroup}}.yaml - - $values/{{ .metadata.annotations.addons_repo_basepath }}/{{ .metadata.labels.tenant }}/defaults/{{ .values.chartName }}/{{.values.applicationSetGroup}}.yaml - - $values/{{ .metadata.annotations.addons_repo_basepath }}/{{ .metadata.labels.tenant }}/clusters/{{ .name }}/{{ .values.chartName }}/{{.values.applicationSetGroup}}.yaml - - $values/{{ .metadata.annotations.addons_repo_basepath }}/{{ .metadata.labels.tenant }}/environments/{{ .metadata.labels.environment }}/{{ .values.chartName }}/{{.values.applicationSetGroup}}.yaml \ No newline at end of file + - $addonsValues/addons/bootstrap/defaults/{{.values.applicationSetGroup}}.yaml + {{- range $repoName := $repoNames }} + {{- $repoRef := printf "%sValues" $repoName }} + {{- $basePath := default (index $.metadata.annotations (printf "%s_repo_basepath" $repoName)) (index $ (printf "%s_repo_basepath" $repoName)) }} + {{- range $pattern := $pathPatterns }} + - ${{ $repoRef }}/{{ $basePath }}/{{ $pattern }}/{{ $commonValuesPath }} + {{- end }} + {{- end }} diff --git a/charts/application-sets/examples/monitoring.yaml b/charts/application-sets/examples/monitoring.yaml index 3b9829b..d73017a 100644 --- a/charts/application-sets/examples/monitoring.yaml +++ b/charts/application-sets/examples/monitoring.yaml @@ -10,71 +10,71 @@ spec: goTemplateOptions: - missingkey=error generators: - - matrix: - generators: - - matrix: - generators: - - clusters: - selector: - matchLabels: - fleet_member: hub-cluster - values: - chartName: application-sets - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" - chartPath: "charts/application-sets" - applicationSetGroup: "monitoring" - groupRelease: '{{default "" (index .metadata.labels "monitoringRelease")}}' - useSelectors: "false" - useVersionSelectors: "true" - - git: - repoURL: '{{ .metadata.annotations.fleet_repo_url }}' - revision: '{{ .metadata.annotations.fleet_repo_revision }}' - files: - - path: "{{ .metadata.annotations.fleet_repo_basepath }}/bootstrap/versions/applicationSets.yaml" - - list: - elementsYaml: | - {{- $releaseTypes := index .releases .values.applicationSetGroup | toJson | fromJson -}} - {{- $result := list -}} - {{- $defaultVersion := dict -}} - {{- /* Defining the Default Version in case we need to fall back */ -}} - {{- range $releaseType := $releaseTypes -}} - {{- if eq $releaseType.type "default" -}} - {{- $defaultVersion = $releaseType -}} - {{- end -}} + - matrix: + generators: + - matrix: + generators: + - clusters: + selector: + matchLabels: + fleet_member: hub-cluster + values: + chartName: application-sets + chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" + chartPath: "charts/application-sets" + applicationSetGroup: "monitoring" + groupRelease: '{{default "" (index .metadata.labels "monitoringRelease")}}' + useSelectors: "true" + useVersionSelectors: "true" + - git: + repoURL: "{{ .metadata.annotations.fleet_repo_url }}" + revision: "{{ .metadata.annotations.fleet_repo_revision }}" + files: + - path: "{{ .metadata.annotations.fleet_repo_basepath }}/bootstrap/versions/applicationSets.yaml" + - list: + elementsYaml: | + {{- $globals := .releases.globals -}} + {{- $releaseNames := index .releases .values.applicationSetGroup | toJson | fromJson -}} + {{- $groupRelease := .values.groupRelease}} + {{- $firstRelease := index $releaseNames 0 -}} + {{- $result := list -}} + {{- /* If the values of the group release is empty or we dont use version selectors then we use only the first element of the list */ -}} + {{- if or (eq $groupRelease "") (eq .values.useVersionSelectors "false") -}} + {{- $mergedRelease := merge $firstRelease $globals -}} + {{- $mergedRelease = merge $mergedRelease (dict "totalReleases" (len $releaseNames)) -}} + {{- $result = append $result $mergedRelease -}} + {{- else -}} + {{- /* We look for the defined releases */ -}} + {{- $found := false -}} + {{- range $releaseName := $releaseNames -}} + {{- if eq $releaseName.type $groupRelease -}} + {{- $found = true -}} {{- end -}} - {{- /* We look for the defined releases */ -}} - {{- range $releaseType := $releaseTypes -}} - {{- /* Case 1: If selectors is true, include all group releases */ -}} - {{- if eq $.values.useSelectors "true" -}} - {{- $result = append $result $releaseType -}} - {{- /* Case 2: If group version Release value exists, only include matching releases */ -}} - {{- else if $.values.groupRelease -}} - {{- if or (not $releaseType.type) (eq $releaseType.type $.values.groupRelease) -}} - {{- $result = append $result $releaseType -}} - {{- end -}} - {{- /* Case 3: Default case - include version if it's the default type */ -}} - {{- else -}} - {{- if eq $releaseType.type "default" -}} - {{- $result = append $result $releaseType -}} - {{- end -}} - {{- end -}} + {{- end -}} + {{- if $found -}} + {{- range $releaseName := $releaseNames -}} + {{- $mergedReleaseValues := merge $releaseName $globals -}} + {{- $mergedReleaseValues = merge $mergedReleaseValues (dict "totalReleases" (len $releaseNames)) -}} + {{- $result = append $result $mergedReleaseValues -}} {{- end -}} - {{- /* If no releases were selected, use default */ -}} - {{- if eq (len $result) 0 -}} - {{- $result = append $result $defaultVersion -}} - {{- end -}} - {{ $result | toJson }} + {{- else -}} + {{- $mergedRelease := merge $firstRelease $globals -}} + {{- $mergedRelease = merge $mergedRelease (dict "totalReleases" (len $releaseNames)) -}} + {{- $result = append $result $mergedRelease -}} + {{- end -}} + {{- end -}} + {{ $result | toJson }} ################################################### #base template (everything common) ################################################### template: metadata: - name: 'cluster-{{.values.applicationSetGroup}}-{{.name}}-{{.type | lower }}' + name: "cluster-{{.values.applicationSetGroup}}-{{.name}}-{{.type | lower }}" spec: project: default destination: namespace: argocd - name: '{{ .name }}' + name: "{{ .name }}" # syncPolicy is identical for both variants syncPolicy: automated: @@ -90,31 +90,61 @@ spec: # conditional sources ################################################### templatePatch: | + {{- $commonValuesPath := printf "%s/%s.yaml" .values.chartName .values.applicationSetGroup -}} + {{- $repoNames := list "addons" -}} + + {{- $environment := .metadata.labels.environment -}} + + {{- $tenantPath := "" -}} + {{- if and (hasKey . "tenant") .tenant -}} + {{- $tenantPath = printf "%s" .tenant -}} + {{- else if (index .metadata.labels "tenant") -}} + {{- $tenantPath = printf "%s" .metadata.labels.tenant -}} + {{- end -}} + + + {{- $clusterName := "" -}} + {{- if and (hasKey . "clusterName") .clusterName -}} + {{- $clusterName = .clusterName -}} + {{- else -}} + {{- $clusterName = .name -}} + {{- end -}} + + {{- $pathPatterns := list + (printf "%s/defaults" $tenantPath) + (printf "%s/environments/%s/defaults" $tenantPath $environment) + (printf "%s/environments/%s/clusters/%s" $tenantPath $environment $clusterName) + -}} + spec: sources: - - ref: values - repoURL: '{{ .metadata.annotations.addons_repo_url }}' - targetRevision: '{{ .metadata.annotations.addons_repo_revision }}' + {{- range $repoName := $repoNames }} + - repoURL: '{{default (index $.metadata.annotations (printf "%s_repo_url" $repoName)) (index $ "repoUrl")}}' + targetRevision: '{{default (index $.metadata.annotations (printf "%s_repo_revision" $repoName)) (index $ "targetRevision")}}' + ref: {{$repoName}}Values + {{- end }} {{- if eq .use_helm_repo_path "false" }} - repoURL: '{{default .values.chartRepo .chartRepo }}' chart: '{{ default .values.chartName .ecrChartName }}' targetRevision: '{{.version}}' {{- else }} - - repoURL: '{{ .metadata.annotations.addons_repo_url }}' - path: '{{ .values.chartPath }}' - targetRevision: '{{ .metadata.annotations.addons_repo_revision }}' + - repoURL: '{{default (index .metadata.annotations "chartRepoUrl") (index . "chartRepoUrl") }}' + path: '{{ default .values.chartPath (index . "chartRepoPath")}}' + targetRevision: '{{default (index .metadata.annotations "chartRepoRevision") (index . "chartRepoRevision") }}' {{- end }} helm: ignoreMissingValueFiles: true valuesObject: - useSelectors: false + useSelectors: '{{.values.useSelectors}}' useVersionSelectors: '{{.values.useVersionSelectors}}' - # Defining the way to group monitoring This application set will handly monitoring and ACK values + applicationSetGroup: {{.values.applicationSetGroup}} + # Defining the way to group addons This application set will handly Addons and ACK values mergeValues: monitoring: use: true releaseType: '{{.type | lower }}' - {{- if eq .values.useVersionSelectors "true"}} + # If we are using version selector we add the version of the releases on the matchlabels + {{- if and (eq .values.useVersionSelectors "true") (or (eq .type .values.groupRelease) (gt (int .totalReleases) 1)) }} releases: {{.values.applicationSetGroup}}Release: '{{.type | lower}}' {{- end }} @@ -122,11 +152,13 @@ spec: globalSelectors: fleet_member: hub-cluster {{- end }} - valueFiles: - - defaults/{{.values.applicationSetGroup}} - - clusters/{{`{{ .nameNormalized }}`}}/{{.values.applicationSetGroup}} + # Those are the Value files to read for the Whole group of applciationsdts valueFiles: - - $values/{{ .metadata.annotations.addons_repo_basepath }}/bootstrap/defaults/{{.values.applicationSetGroup}}.yaml - - $values/{{ .metadata.annotations.addons_repo_basepath }}/{{ .metadata.labels.tenant }}/defaults/{{ .values.chartName }}/{{.values.applicationSetGroup}}.yaml - - $values/{{ .metadata.annotations.addons_repo_basepath }}/{{ .metadata.labels.tenant }}/clusters/{{ .name }}/{{ .values.chartName }}/{{.values.applicationSetGroup}}.yaml - - $values/{{ .metadata.annotations.addons_repo_basepath }}/{{ .metadata.labels.tenant }}/environments/{{ .metadata.labels.environment }}/{{ .values.chartName }}/{{.values.applicationSetGroup}}.yaml \ No newline at end of file + - $addonsValues/addons/bootstrap/defaults/{{.values.applicationSetGroup}}.yaml + {{- range $repoName := $repoNames }} + {{- $repoRef := printf "%sValues" $repoName }} + {{- $basePath := default (index $.metadata.annotations (printf "%s_repo_basepath" $repoName)) (index $ (printf "%s_repo_basepath" $repoName)) }} + {{- range $pattern := $pathPatterns }} + - ${{ $repoRef }}/{{ $basePath }}/{{ $pattern }}/{{ $commonValuesPath }} + {{- end }} + {{- end }} diff --git a/charts/application-sets/examples/versions/applicationSets.yaml b/charts/application-sets/examples/versions/applicationSets.yaml index faa0113..b6e9f8e 100644 --- a/charts/application-sets/examples/versions/applicationSets.yaml +++ b/charts/application-sets/examples/versions/applicationSets.yaml @@ -1,31 +1,22 @@ # applicationSets.yaml releases: - addons: - - type: "default" - use_helm_repo_path: "true" - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" - ecrChartName: "application-sets" - version: 0.1.0 - - type: "release1" - use_helm_repo_path: "false" - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" + globals: + # if we use the chart repo we need to be conected to remote + chartRepoUrl: https://github.com/markoskandylis/gitops-bridge-helm-charts + chartRepoPath: application-sets + chartRepoRevision: main + chartRepo: "12345678901.dkr.ecr.eu-west-2.amazonaws.com" ecrChartName: "application-sets" - version: 0.2.0 + version: 0.3.1 + addons: + - releaseName: "default" + use_helm_repo_path: "true" monitoring: - - type: "default" - use_helm_repo_path: "true" - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" - ecrChartName: "application-sets" - version: 0.1.0 + - releaseName: "default" + use_helm_repo_path: "true" resources: - - type: "default" - use_helm_repo_path: "true" - ecrChartName: "application-sets" - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" - version: 0.1.0 + - releaseName: "default" + use_helm_repo_path: "true" fleet: - - type: "default" - use_helm_repo_path: "true" - ecrChartName: "application-sets" - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" - version: 0.1.0 \ No newline at end of file + - releaseName: "default" + use_helm_repo_path: "true" From f512447450c773c9b93e124d5ea0d12068793220 Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 13:46:14 +0100 Subject: [PATCH 03/11] fixing the example with proper chart path Signed-off-by: Markos Kandylis --- charts/application-sets/examples/versions/applicationSets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/application-sets/examples/versions/applicationSets.yaml b/charts/application-sets/examples/versions/applicationSets.yaml index b6e9f8e..60e7f2f 100644 --- a/charts/application-sets/examples/versions/applicationSets.yaml +++ b/charts/application-sets/examples/versions/applicationSets.yaml @@ -3,7 +3,7 @@ releases: globals: # if we use the chart repo we need to be conected to remote chartRepoUrl: https://github.com/markoskandylis/gitops-bridge-helm-charts - chartRepoPath: application-sets + chartRepoPath: charts/application-sets chartRepoRevision: main chartRepo: "12345678901.dkr.ecr.eu-west-2.amazonaws.com" ecrChartName: "application-sets" From f03d81f7c619034c63d1a80cfbb0d38b511716c4 Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 13:50:51 +0100 Subject: [PATCH 04/11] Updating the addons and values to create argocd ingress if selected Signed-off-by: Markos Kandylis --- charts/application-sets/values/addons.yaml | 79 +++++++++---------- .../values/fleetBootstrap.yaml | 15 ++-- .../application-sets/values/monitoring.yaml | 27 +++---- charts/argocd-ingress/.helmignore | 23 ++++++ charts/argocd-ingress/Chart.yaml | 24 ++++++ charts/argocd-ingress/templates/_helpers.tpl | 62 +++++++++++++++ .../argocd-ingress/templates/argoService.yaml | 24 ++++++ charts/argocd-ingress/templates/ingress.yaml | 75 ++++++++++++++++++ .../templates/ingressClass.yaml | 29 +++++++ .../templates/ingressClassParams.yaml | 14 ++++ charts/argocd-ingress/values.yaml | 15 ++++ 11 files changed, 323 insertions(+), 64 deletions(-) create mode 100644 charts/argocd-ingress/.helmignore create mode 100644 charts/argocd-ingress/Chart.yaml create mode 100644 charts/argocd-ingress/templates/_helpers.tpl create mode 100644 charts/argocd-ingress/templates/argoService.yaml create mode 100644 charts/argocd-ingress/templates/ingress.yaml create mode 100644 charts/argocd-ingress/templates/ingressClass.yaml create mode 100644 charts/argocd-ingress/templates/ingressClassParams.yaml create mode 100644 charts/argocd-ingress/values.yaml diff --git a/charts/application-sets/values/addons.yaml b/charts/application-sets/values/addons.yaml index f847139..8617cb5 100644 --- a/charts/application-sets/values/addons.yaml +++ b/charts/application-sets/values/addons.yaml @@ -6,48 +6,44 @@ argocd: releaseName: argocd defaultVersion: "7.7.8" chartRepository: "https://argoproj.github.io/argo-helm" + additionalResources: + path: "charts/argocd-ingress" + type: "ingress" + helm: + releaseName: ingress + valuesObject: + ingressClass: + useAutomode: '{{default "false" (index .metadata.annotations "enable_automode")}}' + ingress: + domain: '{{default "" (index .metadata.annotations "argocd_dns")}}' + privateCertificate: '{{default "" (index .metadata.annotations "argocd_ingress_certificate")}}' + rules: + - host: '{{default "argocd.example.com" (index .metadata.annotations "argocd_dns")}}' selector: matchExpressions: - key: enable_argocd operator: In - values: ['true'] -route53-chart: - enabled: true - enableAckPodIdentity: false - namespace: ack-system - chartName: route53-chart - defaultVersion: "0.0.20" - chartNamespace: aws-controllers-k8s - chartRepository: public.ecr.aws - selector: - matchExpressions: - - key: enable_route53_controller - operator: In - values: ['true'] + values: ["true"] valuesObject: - aws: - region: '{{.metadata.annotations.aws_region}}' - serviceAccount: - name: 'route53-controller' - annotations: - eks.amazonaws.com/role-arn: '{{default "" (index .metadata.annotations "ack_route53_controller_role_arn")}}' + global: + domain: '{{default "" (index .metadata.annotations "argocd_dns")}}' external-secrets: enabled: true enableAckPodIdentity: false namespace: external-secrets chartName: external-secrets - defaultVersion: "0.10.3" + defaultVersion: "0.17.0" chartRepository: "https://charts.external-secrets.io" additionalResources: path: "charts/fleet-secret" type: "ecr-token" helm: - releaseName: ecr-token + releaseName: ecr-token selector: matchExpressions: - key: enable_external_secrets operator: In - values: ['true'] + values: ["true"] valuesObject: serviceAccount: name: "external-secrets-sa" @@ -63,22 +59,22 @@ aws-load-balancer-controller: matchExpressions: - key: enable_aws_load_balancer_controller operator: In - values: ['true'] + values: ["true"] valuesObject: serviceAccount: - name: "aws-load-balancer-controller-sa" - vpcId: '{{.metadata.annotations.aws_vpc_id}}' - clusterName: '{{.name}}' + name: "aws-load-balancer-controller-sa" + vpcId: "{{.metadata.annotations.aws_vpc_id}}" + clusterName: "{{.name}}" ignoreDifferences: - kind: Secret name: aws-load-balancer-tls jsonPointers: [/data] - group: admissionregistration.k8s.io kind: MutatingWebhookConfiguration - jqPathExpressions: ['.webhooks[].clientConfig.caBundle'] + jqPathExpressions: [".webhooks[].clientConfig.caBundle"] - group: admissionregistration.k8s.io kind: ValidatingWebhookConfiguration - jqPathExpressions: ['.webhooks[].clientConfig.caBundle'] + jqPathExpressions: [".webhooks[].clientConfig.caBundle"] metrics-server: enabled: true enableAckPodIdentity: false @@ -89,12 +85,12 @@ metrics-server: matchExpressions: - key: enable_metrics_server operator: In - values: ['true'] + values: ["true"] karpenter: enabled: false enableAckPodIdentity: false releaseName: karpenter - namespace: 'kube-system' + namespace: "kube-system" chartNamespace: karpenter chartName: karpenter chartRepository: public.ecr.aws @@ -103,10 +99,10 @@ karpenter: matchExpressions: - key: enable_karpenter operator: In - values: ['true'] + values: ["true"] valuesObject: settings: - clusterName: '{{.metadata.annotations.aws_cluster_name}}' + clusterName: "{{.metadata.annotations.aws_cluster_name}}" interruptionQueue: '{{default "" (index .metadata.annotations "karpenter_sqs_queue_name")}}' serviceAccount: name: '{{default "karpenter" (index .metadata.annotations "karpenter_service_account")}}' @@ -124,7 +120,7 @@ aws_efs_csi_driver: matchExpressions: - key: enable_aws_efs_csi_driver operator: In - values: ['true'] + values: ["true"] valuesObject: controller: serviceAccount: @@ -148,7 +144,7 @@ cert_manager: matchExpressions: - key: enable_cert_manager operator: In - values: ['true'] + values: ["true"] valuesObject: installCRDs: true serviceAccount: @@ -162,19 +158,18 @@ external-dns: namespace: '{{default "external-dns" (index .metadata.annotations "external_dns_namespace") }}' chartName: external-dns chartRepository: https://kubernetes-sigs.github.io/external-dns - defaultVersion: "1.14.3" + defaultVersion: "1.16.1" selector: matchExpressions: - key: enable_external_dns operator: In - values: ['true'] + values: ["true"] valuesObject: provider: aws serviceAccount: name: '{{default "" (index .metadata.annotations "external_dns_service_account") }}' annotations: - eks.amazonaws.com/role-arn: '{{default "" (index .metadata.annotations "external_dns_iam_role_arn") }}' - domainFilters: '{{.metadata.annotations.external_dns_domain_filters}}' - txtOwnerId: '{{.metadata.annotations.aws_cluster_name}}' - policy: '{{default "upsert-only" .metadata.annotations.external_dns_policy}}' - extraArgs: '{{default "[]" .metadata.annotations.external_dns_extra_args}}' \ No newline at end of file + eks.amazonaws.com/role-arn: '{{default "" (index .metadata.annotations "external_dns_iam_role_arn")}}' + domainFilters: ["{{.metadata.annotations.external_dns_domain_filters}}"] + txtOwnerId: "{{.metadata.annotations.aws_cluster_name}}" + policy: '{{default "upsert-only" (index .metadata.annotations "external_dns_policy")}}' diff --git a/charts/application-sets/values/fleetBootstrap.yaml b/charts/application-sets/values/fleetBootstrap.yaml index f2e8063..39d54a7 100644 --- a/charts/application-sets/values/fleetBootstrap.yaml +++ b/charts/application-sets/values/fleetBootstrap.yaml @@ -4,20 +4,20 @@ fleet-external-secrets: namespace: platform-system releaseName: fleet-external-secrets chartName: external-secrets - defaultVersion: "0.10.3" + defaultVersion: "0.18.0" chartRepository: "https://charts.external-secrets.io" additionalResources: path: "charts/fleet-secret" type: "ecr-token" helm: - releaseName: ecr-token + releaseName: ecr-token valuesObject: serviceAccount: name: "external-secrets-sa" scopedNamespace: "platform-system" scopedRBAC: true fleet-argocd: - enabled: true + enabled: true enableAckPodIdentity: false chartName: argo-cd namespace: platform-system @@ -33,9 +33,9 @@ fleet-iam-chart: chartRepository: public.ecr.aws valuesObject: aws: - region: '{{.metadata.annotations.aws_region}}' + region: "{{.metadata.annotations.aws_region}}" serviceAccount: - name: '{{.metadata.annotations.ack_iam_service_account}}' + name: "{{.metadata.annotations.ack_iam_service_account}}" fleet-ack-eks: chartName: eks-chart namespace: ack-system @@ -45,7 +45,6 @@ fleet-ack-eks: chartRepository: public.ecr.aws valuesObject: aws: - region: '{{.metadata.annotations.aws_region}}' + region: "{{.metadata.annotations.aws_region}}" serviceAccount: - name: '{{.metadata.annotations.ack_eks_service_account}}' - \ No newline at end of file + name: "{{.metadata.annotations.ack_eks_service_account}}" diff --git a/charts/application-sets/values/monitoring.yaml b/charts/application-sets/values/monitoring.yaml index 811a0ec..1ae5345 100644 --- a/charts/application-sets/values/monitoring.yaml +++ b/charts/application-sets/values/monitoring.yaml @@ -5,12 +5,12 @@ cw-prometheus: namespace: '{{default "amazon-cloudwatch" (index .metadata.annotations "cw_prometheus_namespace")}}' chartName: cw-prometheus defaultVersion: "0.1.0" - path: 'charts/cw-prometheus' + path: "charts/cw-prometheus" selector: matchExpressions: - key: enable_cw_prometheus operator: In - values: ['true'] + values: ["true"] kube-prometheus-stack: enabled: true enableAckPodIdentity: false @@ -22,7 +22,7 @@ kube-prometheus-stack: matchExpressions: - key: enable_kube_prometheus_stack operator: In - values: ['true'] + values: ["true"] cni-metrics-helper: enabled: true enableAckPodIdentity: false @@ -32,14 +32,14 @@ cni-metrics-helper: defaultVersion: "1.18.5" valuesObject: env: - AWS_CLUSTER_ID: '{{.metadata.annotations.aws_cluster_name}}' + AWS_CLUSTER_ID: "{{.metadata.annotations.aws_cluster_name}}" selector: matchExpressions: - key: enable_cni_metrics_helper operator: In - values: ['true'] + values: ["true"] amp-prometheus: - enabled: true + enabled: false enableAckPodIdentity: false releaseName: amp-prometheus namespace: '{{default "amp-prometheus" (index .metadata.annotations "amp_prometheus_namespace")}}' @@ -50,17 +50,16 @@ amp-prometheus: matchExpressions: - key: enable_amp_prometheus operator: In - values: ['true'] + values: ["true"] valuesObject: serviceAccounts: server: - name: '{{default "amp-prometheus-server-sa" (index .metadata.annotations "amp_prometheus_server_sa")}}' - annotations: - eks.amazonaws.com/role-arn: '{{default "" (index .metadata.annotations "amp_prometheus_iam_role_arn")}}' + name: '{{default "amp-prometheus-server-sa" (index .metadata.annotations "amp_prometheus_server_sa")}}' + annotations: + eks.amazonaws.com/role-arn: '{{default "" (index .metadata.annotations "amp_prometheus_iam_role_arn")}}' server: remoteWrite: - - - url: '{{(index .metadata.annotations "amp_endpoint_url")}}.api/v1/remote_write' + - url: '{{(index .metadata.annotations "amp_endpoint_url")}}.api/v1/remote_write' sigv4: - region: '{{.metadata.annotations.aws_region}}' - role_arn: '{{default "" (index .metadata.annotations "amp_prometheus_crossaccount_role")}}' + region: "{{.metadata.annotations.aws_region}}" + role_arn: '{{default "" (index .metadata.annotations "amp_prometheus_crossaccount_role")}}' diff --git a/charts/argocd-ingress/.helmignore b/charts/argocd-ingress/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/argocd-ingress/.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/charts/argocd-ingress/Chart.yaml b/charts/argocd-ingress/Chart.yaml new file mode 100644 index 0000000..d148d87 --- /dev/null +++ b/charts/argocd-ingress/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: argo-ingress +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +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/charts/argocd-ingress/templates/_helpers.tpl b/charts/argocd-ingress/templates/_helpers.tpl new file mode 100644 index 0000000..f7e078c --- /dev/null +++ b/charts/argocd-ingress/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "argo-ingress.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 "argo-ingress.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 "argo-ingress.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "argo-ingress.labels" -}} +helm.sh/chart: {{ include "argo-ingress.chart" . }} +{{ include "argo-ingress.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "argo-ingress.selectorLabels" -}} +app.kubernetes.io/name: {{ include "argo-ingress.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "argo-ingress.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "argo-ingress.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/charts/argocd-ingress/templates/argoService.yaml b/charts/argocd-ingress/templates/argoService.yaml new file mode 100644 index 0000000..cc82771 --- /dev/null +++ b/charts/argocd-ingress/templates/argoService.yaml @@ -0,0 +1,24 @@ +# ArgoCD Service Required for Ingress with AWS alb Controller +{{- if eq .Values.enableIngress "true" }} +{{- if .Values.argoService }} +apiVersion: v1 +kind: Service +metadata: + annotations: + argocd.argoproj.io/sync-wave: "1" + alb.ingress.kubernetes.io/backend-protocol-version: HTTP2 #This tells AWS to send traffic from the ALB using HTTP2. Can use GRPC as well if you want to leverage GRPC specific features + labels: + app: argogrpc + name: {{.Values.argoService.argoIngressServiceName }} +spec: + ports: + - name: "443" + port: 443 + protocol: TCP + targetPort: 8080 + selector: + app.kubernetes.io/name: {{.Values.argoService.argoServerSericeName }} + sessionAffinity: None + type: NodePort +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/argocd-ingress/templates/ingress.yaml b/charts/argocd-ingress/templates/ingress.yaml new file mode 100644 index 0000000..476d63a --- /dev/null +++ b/charts/argocd-ingress/templates/ingress.yaml @@ -0,0 +1,75 @@ +{{- if eq .Values.enableIngress "true" }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "argo-ingress.fullname" . }} + annotations: + {{- if .Values.ingress.annotations }} + {{- .Values.ingress.annotations | toYaml | nindent 4 }} + {{- end }} + argocd.argoproj.io/sync-wave: "4" + {{- if .Values.ingress.useExternalDns}} + external-dns.alpha.kubernetes.io/hostname: {{ .Values.ingress.domain }} + external-dns.alpha.kubernetes.io/ingress-hostname-source: annotation-only + external-dns.alpha.kubernetes.io/ttl: "300" + {{- end }} + alb.ingress.kubernetes.io/backend-protocol: {{ default "HTTPS" .Values.ingress.backendProtocol }} + alb.ingress.kubernetes.io/load-balancer-attributes: routing.http.drop_invalid_header_fields.enabled={{default false .Values.ingress.dropHttpHeader}} + alb.ingress.kubernetes.io/target-type: {{ default "ip" .Values.ingress.targetType }} + alb.ingress.kubernetes.io/inbound-cidrs: {{ default "0.0.0.0/0" .Values.ingress.inboundCidrs }} + {{- if .Values.ingress.grpcService }} + alb.ingress.kubernetes.io/conditions.{{ .Values.ingress.grpcService }}: | + [{"field":"http-header","httpHeaderConfig":{"httpHeaderName": "Content-Type", "values":["application/grpc"]}}] + {{- end }} + {{- if .Values.ingress.privateCertificate }} + alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.privateCertificate }} + {{- end }} + alb.ingress.kubernetes.io/listen-ports: '{{ default "[{\"HTTPS\":443}]" .Values.ingress.listenPorts }}' + alb.ingress.kubernetes.io/ssl-policy: {{ default "ELBSecurityPolicy-TLS13-1-2-2021-06" .Values.ingress.sslPolicy }} + {{- if .Values.ingress.tls }} + alb.ingress.kubernetes.io/certificate-arn: {{ .Values.ingress.tls.certificateArn }} + {{- if .Values.ingress.tls.sslPolicy }} + {{- end }} + {{- end }} +spec: + ingressClassName: {{ default "alb" .Values.ingressClass.name }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls.hosts }} + - hosts: + - {{ . }} + {{- if $.Values.ingress.tls.secretName }} + secretName: {{ $.Values.ingress.tls.secretName }} + {{- end }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.rules }} + - host: {{ .host }} + http: + paths: + {{- if .paths }} + {{- range .paths }} + - backend: + service: + name: {{ .serviceName }} + port: + number: {{ .port | default 443 }} + pathType: {{ .pathType | default "ImplementationSpecific" }} + {{- end }} + {{- else }} + - backend: + service: + name: argogrpc + port: + number: 443 + pathType: ImplementationSpecific + - backend: + service: + name: argocd-server + port: + number: 443 + pathType: ImplementationSpecific + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/argocd-ingress/templates/ingressClass.yaml b/charts/argocd-ingress/templates/ingressClass.yaml new file mode 100644 index 0000000..a5a6577 --- /dev/null +++ b/charts/argocd-ingress/templates/ingressClass.yaml @@ -0,0 +1,29 @@ +{{- if eq .Values.enableIngress "true" }} +apiVersion: networking.k8s.io/v1 +kind: IngressClass +metadata: + name: {{.Values.ingressClass.name}} + annotations: + {{- if .Values.ingressClass.annotations }} + {{- .Values.ingressClass.annotations | toYaml | nindent 10 }} + {{- end }} + # Use this annotation to set an IngressClass as Default + # If an Ingress doesn't specify a class, it will use the Default + ingressclass.kubernetes.io/is-default-class: {{ default "true" .Values.ingressClass.isDefault | quote }} + argocd.argoproj.io/sync-wave: "3" +spec: + controller: {{- if .Values.ingressClass.useAutomode }} + "eks.amazonaws.com/alb" + {{- else }} + {{ required "When useAutomode is false, you must specify a valid controllerType (e.g., 'controllerType: ingress.k8s.aws/alb')" .Values.ingressClass.controllerType }} + {{- end }} + parameters: + apiGroup: {{- if .Values.ingressClass.useAutomode }} + "eks.amazonaws.com" + {{- else }} + "elbv2.k8s.aws" + {{- end }} + kind: IngressClassParams + # Use the name of the IngressClassParams set in the previous step + name: {{ .Values.ingressClass.name }} +{{- end }} \ No newline at end of file diff --git a/charts/argocd-ingress/templates/ingressClassParams.yaml b/charts/argocd-ingress/templates/ingressClassParams.yaml new file mode 100644 index 0000000..012b519 --- /dev/null +++ b/charts/argocd-ingress/templates/ingressClassParams.yaml @@ -0,0 +1,14 @@ +{{- if eq .Values.enableIngress "true" }} + apiVersion: {{- if .Values.ingressClass.useAutomode }} + "eks.amazonaws.com/v1" + {{- else }} + "elbv2.k8s.aws/v1beta1" + {{- end }} +kind: IngressClassParams +metadata: + annotations: + argocd.argoproj.io/sync-wave: "2" + name: {{.Values.ingressClass.name}} +spec: + scheme: {{.Values.ingressClass.scheme}} +{{- end }} \ No newline at end of file diff --git a/charts/argocd-ingress/values.yaml b/charts/argocd-ingress/values.yaml new file mode 100644 index 0000000..6ef5024 --- /dev/null +++ b/charts/argocd-ingress/values.yaml @@ -0,0 +1,15 @@ +enableIngress: "true" +ingressClass: + name: alb + scheme: internal + controllerType: "" +argoService: + argoIngressServiceName: argogrpc + argoServerSericeName: argocd-server +ingress: + grpcService: argogrpc + inboundCidrs: "0.0.0.0/0" + dropHttpHeader: false + privateCertificate: "" + rules: + - host: "" From 18f324304b502800d8f154cd32edbe1e347419a4 Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 14:00:45 +0100 Subject: [PATCH 05/11] Updated the type to releaseName Signed-off-by: Markos Kandylis --- .../application-sets/examples/monitoring.yaml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/charts/application-sets/examples/monitoring.yaml b/charts/application-sets/examples/monitoring.yaml index d73017a..da9e782 100644 --- a/charts/application-sets/examples/monitoring.yaml +++ b/charts/application-sets/examples/monitoring.yaml @@ -1,7 +1,7 @@ apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: - name: cluster-monitoring + name: cluster-addons namespace: argocd spec: syncPolicy: @@ -20,11 +20,11 @@ spec: fleet_member: hub-cluster values: chartName: application-sets - chartRepo: "12345678910.dkr.ecr.eu-west-2.amazonaws.com" + chartRepo: "1234567890.dkr.ecr.eu-west-2.amazonaws.com" chartPath: "charts/application-sets" applicationSetGroup: "monitoring" groupRelease: '{{default "" (index .metadata.labels "monitoringRelease")}}' - useSelectors: "true" + useSelectors: "false" useVersionSelectors: "true" - git: repoURL: "{{ .metadata.annotations.fleet_repo_url }}" @@ -47,7 +47,7 @@ spec: {{- /* We look for the defined releases */ -}} {{- $found := false -}} {{- range $releaseName := $releaseNames -}} - {{- if eq $releaseName.type $groupRelease -}} + {{- if eq $releaseName.releaseName $groupRelease -}} {{- $found = true -}} {{- end -}} {{- end -}} @@ -69,7 +69,7 @@ spec: ################################################### template: metadata: - name: "cluster-{{.values.applicationSetGroup}}-{{.name}}-{{.type | lower }}" + name: "cluster-{{.values.applicationSetGroup}}-{{.name}}-{{.releaseName | lower }}" spec: project: default destination: @@ -138,15 +138,16 @@ spec: useSelectors: '{{.values.useSelectors}}' useVersionSelectors: '{{.values.useVersionSelectors}}' applicationSetGroup: {{.values.applicationSetGroup}} + groupRelease: '{{.values.groupRelease}}' # Defining the way to group addons This application set will handly Addons and ACK values mergeValues: monitoring: use: true - releaseType: '{{.type | lower }}' + releaseName: '{{default "" .releaseName | lower }}' # If we are using version selector we add the version of the releases on the matchlabels - {{- if and (eq .values.useVersionSelectors "true") (or (eq .type .values.groupRelease) (gt (int .totalReleases) 1)) }} + {{- if and (eq .values.useVersionSelectors "true") (or (eq .releaseName .values.groupRelease) (gt (int .totalReleases) 1)) }} releases: - {{.values.applicationSetGroup}}Release: '{{.type | lower}}' + {{.values.applicationSetGroup}}Release: '{{.releaseName | lower}}' {{- end }} {{- if eq .values.useSelectors "false"}} globalSelectors: From c2bf5903404c1cb16e230fa99a61d8180d3e59d0 Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 14:03:37 +0100 Subject: [PATCH 06/11] Fixed the name of the monitoring appset Signed-off-by: Markos Kandylis --- charts/application-sets/examples/monitoring.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/application-sets/examples/monitoring.yaml b/charts/application-sets/examples/monitoring.yaml index da9e782..3b84847 100644 --- a/charts/application-sets/examples/monitoring.yaml +++ b/charts/application-sets/examples/monitoring.yaml @@ -1,7 +1,7 @@ apiVersion: argoproj.io/v1alpha1 kind: ApplicationSet metadata: - name: cluster-addons + name: cluster-monitoring namespace: argocd spec: syncPolicy: From 0c4b56b029dd0ffd204da6701d6b71a113239b1b Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 14:07:05 +0100 Subject: [PATCH 07/11] Updated the values of charts for laterst Signed-off-by: Markos Kandylis --- charts/application-sets/values/addons.yaml | 8 ++++---- .../values/fleetBootstrap.yaml | 18 +++++++++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/charts/application-sets/values/addons.yaml b/charts/application-sets/values/addons.yaml index 8617cb5..e0aaa19 100644 --- a/charts/application-sets/values/addons.yaml +++ b/charts/application-sets/values/addons.yaml @@ -4,7 +4,7 @@ argocd: chartName: argo-cd namespace: argocd releaseName: argocd - defaultVersion: "7.7.8" + defaultVersion: "8.1.2" chartRepository: "https://argoproj.github.io/argo-helm" additionalResources: path: "charts/argocd-ingress" @@ -32,7 +32,7 @@ external-secrets: enableAckPodIdentity: false namespace: external-secrets chartName: external-secrets - defaultVersion: "0.17.0" + defaultVersion: "0.18.2" chartRepository: "https://charts.external-secrets.io" additionalResources: path: "charts/fleet-secret" @@ -94,7 +94,7 @@ karpenter: chartNamespace: karpenter chartName: karpenter chartRepository: public.ecr.aws - defaultVersion: "1.3.3" + defaultVersion: "1.4.3" selector: matchExpressions: - key: enable_karpenter @@ -152,7 +152,7 @@ cert_manager: annotations: eks.amazonaws.com/role-arn: '{{default "" (index .metadata.annotations "cert_manager_iam_role_arn") }}' external-dns: - enabled: false + enabled: true enableAckPodIdentity: false releaseName: external-dns namespace: '{{default "external-dns" (index .metadata.annotations "external_dns_namespace") }}' diff --git a/charts/application-sets/values/fleetBootstrap.yaml b/charts/application-sets/values/fleetBootstrap.yaml index 39d54a7..e5e00b5 100644 --- a/charts/application-sets/values/fleetBootstrap.yaml +++ b/charts/application-sets/values/fleetBootstrap.yaml @@ -22,8 +22,24 @@ fleet-argocd: chartName: argo-cd namespace: platform-system releaseName: fleet-argocd - defaultVersion: "7.5.2" + defaultVersion: "8.1.2" chartRepository: "https://argoproj.github.io/argo-helm" + valuesObject: + global: + domain: '{{default "" (index .metadata.annotations "argocd_dns")}}' + additionalResources: + path: "charts/argocd-ingress" + type: "ingress" + helm: + releaseName: ingress + valuesObject: + ingressClass: + useAutomode: '{{default "false" (index .metadata.annotations "enable_automode")}}' + ingress: + domain: '{{default "" (index .metadata.annotations "argocd_dns")}}' + privateCertificate: '{{default "" (index .metadata.annotations "argocd_ingress_certificate")}}' + rules: + - host: '{{default "argocd.example.com" (index .metadata.annotations "argocd_dns")}}' fleet-iam-chart: chartName: iam-chart releaseName: fleet-iam-ack From c1985f1575f4eb5c550b4b8e0438486cc71f040f Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Fri, 3 Oct 2025 15:50:16 +0100 Subject: [PATCH 08/11] Adding global rewrite Signed-off-by: Markos Kandylis --- .../templates/gitExternalSecret.yaml | 17 +++++++++-------- charts/fleet-secret/values.yaml | 4 ---- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/charts/fleet-secret/templates/gitExternalSecret.yaml b/charts/fleet-secret/templates/gitExternalSecret.yaml index 8d9b081..a2ba73c 100644 --- a/charts/fleet-secret/templates/gitExternalSecret.yaml +++ b/charts/fleet-secret/templates/gitExternalSecret.yaml @@ -1,15 +1,16 @@ {{- if .Values.gitExternalSecrets.enabled }} -{{- $globalGitSecrets := .Values.global.gitExternalSecrets.externalSecrets | default dict }} -{{- $clusterGitSecrets := dict }} -{{- if and .Values.tenantGitExternalSecrets .Values.tenantGitExternalSecrets.externalSecrets }} -{{- $clusterGitSecrets = .Values.tenantGitExternalSecrets.externalSecrets }} +{{- $globalGitSecrets := dict }} +{{- if and .Values.global .Values.global.gitExternalSecrets .Values.global.gitExternalSecrets.externalSecrets }} +{{- $globalGitSecrets = .Values.global.gitExternalSecrets.externalSecrets }} {{- end }} -{{- $mergedSecrets := mergeOverwrite $globalGitSecrets $clusterGitSecrets }} -{{- $secretStoreRefName := .Values.global.gitExternalSecrets.secretStoreRefName -}} -{{- $secretStoreRefKind := .Values.global.gitExternalSecrets.secretStoreRefKind -}} -{{- $useHttp := .Values.global.gitExternalSecrets.useHttp -}} +{{- $secretStoreRefName := .Values.global.gitExternalSecrets.secretStoreRefName | default "" -}} +{{- $secretStoreRefKind := .Values.global.gitExternalSecrets.secretStoreRefKind | default "" -}} +{{- $useHttp := .Values.global.gitExternalSecrets.useHttp | default false -}} {{- $useGitHubApp := .Values.global.gitExternalSecrets.useGitHubApp | default false -}} {{- $usePrivateKey := .Values.global.gitExternalSecrets.usePrivateKey | default false -}} +{{- $clusterGitSecrets := .Values.gitExternalSecrets.externalSecrets | default dict }} +{{/*Merging Git External Secrets*/}} +{{- $mergedSecrets := merge $clusterGitSecrets $globalGitSecrets }} {{- range $externalSecretName, $externalSecret := $mergedSecrets }} apiVersion: external-secrets.io/v1 diff --git a/charts/fleet-secret/values.yaml b/charts/fleet-secret/values.yaml index b2b8fcc..bac1125 100644 --- a/charts/fleet-secret/values.yaml +++ b/charts/fleet-secret/values.yaml @@ -1,7 +1,6 @@ # Example of global values to append if we want to add more endpoints withough replciation global: gitExternalSecrets: - enabled: false useGitHubApp: true secretStoreRefName: fleet-git-eks-secret-store secretStoreRefKind: SecretStore @@ -33,9 +32,6 @@ externalSecret: secretManagerSecretName: "" clusterName: "" -tenantGitExternalSecrets: - externalSecrets: {} - ecrAuthenticationToken: enabled: false name: ecr-token-secret From 1f4d2e9444648260ac29c32870d445cc1d09b7cd Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Tue, 7 Oct 2025 13:09:37 +0300 Subject: [PATCH 09/11] Updated argocd to latest helm chart Signed-off-by: Markos Kandylis --- charts/application-sets/values/addons.yaml | 2 +- charts/application-sets/values/fleetBootstrap.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/application-sets/values/addons.yaml b/charts/application-sets/values/addons.yaml index e0aaa19..bfaa79c 100644 --- a/charts/application-sets/values/addons.yaml +++ b/charts/application-sets/values/addons.yaml @@ -4,7 +4,7 @@ argocd: chartName: argo-cd namespace: argocd releaseName: argocd - defaultVersion: "8.1.2" + defaultVersion: "8.5.8" chartRepository: "https://argoproj.github.io/argo-helm" additionalResources: path: "charts/argocd-ingress" diff --git a/charts/application-sets/values/fleetBootstrap.yaml b/charts/application-sets/values/fleetBootstrap.yaml index e5e00b5..0d1e75e 100644 --- a/charts/application-sets/values/fleetBootstrap.yaml +++ b/charts/application-sets/values/fleetBootstrap.yaml @@ -22,7 +22,7 @@ fleet-argocd: chartName: argo-cd namespace: platform-system releaseName: fleet-argocd - defaultVersion: "8.1.2" + defaultVersion: "8.5.8" chartRepository: "https://argoproj.github.io/argo-helm" valuesObject: global: From 52045fd276f5a87829051edf731ef1bb5e4e68be Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Tue, 7 Oct 2025 15:34:31 +0300 Subject: [PATCH 10/11] Removing gitExternal secrets from globals Signed-off-by: Markos Kandylis --- charts/fleet-secret/README.MD | 7 +++++- .../templates/gitExternalSecret.yaml | 22 ++++++------------- charts/fleet-secret/values.yaml | 19 +++++++++++----- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/charts/fleet-secret/README.MD b/charts/fleet-secret/README.MD index 5f0d9b5..7ec5112 100644 --- a/charts/fleet-secret/README.MD +++ b/charts/fleet-secret/README.MD @@ -160,6 +160,11 @@ gitExternalSecrets: secretManagerSecretName: "git-addons-creds" ``` +Values defined under `global.gitExternalSecrets.externalSecrets` act as shared defaults and +are deep-merged with the chart-level `gitExternalSecrets.externalSecrets`. Define the common +portions (for example `secretName`, `secretType`, or annotations) globally and only override +the differing keys per cluster or environment. + ## Notes - All secrets are created in the ArgoCD namespace by default @@ -178,4 +183,4 @@ gitExternalSecrets: ## License This chart is licensed under the Apache License 2.0. -``` \ No newline at end of file +``` diff --git a/charts/fleet-secret/templates/gitExternalSecret.yaml b/charts/fleet-secret/templates/gitExternalSecret.yaml index a2ba73c..4f553e3 100644 --- a/charts/fleet-secret/templates/gitExternalSecret.yaml +++ b/charts/fleet-secret/templates/gitExternalSecret.yaml @@ -1,26 +1,20 @@ {{- if .Values.gitExternalSecrets.enabled }} -{{- $globalGitSecrets := dict }} -{{- if and .Values.global .Values.global.gitExternalSecrets .Values.global.gitExternalSecrets.externalSecrets }} -{{- $globalGitSecrets = .Values.global.gitExternalSecrets.externalSecrets }} -{{- end }} -{{- $secretStoreRefName := .Values.global.gitExternalSecrets.secretStoreRefName | default "" -}} +{{- $secretStoreRefName := .Values.gitExternalSecrets.secretStoreRefName | default "" -}} {{- $secretStoreRefKind := .Values.global.gitExternalSecrets.secretStoreRefKind | default "" -}} {{- $useHttp := .Values.global.gitExternalSecrets.useHttp | default false -}} {{- $useGitHubApp := .Values.global.gitExternalSecrets.useGitHubApp | default false -}} {{- $usePrivateKey := .Values.global.gitExternalSecrets.usePrivateKey | default false -}} {{- $clusterGitSecrets := .Values.gitExternalSecrets.externalSecrets | default dict }} -{{/*Merging Git External Secrets*/}} -{{- $mergedSecrets := merge $clusterGitSecrets $globalGitSecrets }} -{{- range $externalSecretName, $externalSecret := $mergedSecrets }} +{{- range $externalSecretName, $externalSecret := $clusterGitSecrets }} apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: {{ $externalSecretName }} spec: secretStoreRef: - kind: {{ $secretStoreRefKind }} - name: {{ $secretStoreRefName }} + kind: {{ $externalSecret.secretStoreRefKind | default $secretStoreRefKind }} + name: {{ $externalSecret.secretStoreRefName | default $secretStoreRefName }} refreshInterval: "1m" target: name: {{ $externalSecret.secretName }} @@ -37,15 +31,13 @@ spec: {{- else }} url: "{{`{{ .url }}`}}" {{- end }} - {{- if $useHttp }} + {{- if $externalSecret.useHttp | default $useHttp }} username: "{{`{{ .username }}`}}" password: "{{`{{ .password }}`}}" - {{- end }} - {{- if $usePrivateKey }} + {{- else if $externalSecret.usePrivateKey | default $usePrivateKey }} insecureIgnoreHostKey: "true" sshPrivateKey: "{{`{{ .private_key }}`}}" - {{- end }} - {{- if $useGitHubApp }} + {{- else if $externalSecret.useGitHubApp | default $useGitHubApp }} githubAppID: "{{`{{ .github_app_id }}`}}" githubAppInstallationID: "{{`{{ .github_app_installation_id }}`}}" githubAppPrivateKey: "{{`{{ .github_private_key }}`}}" diff --git a/charts/fleet-secret/values.yaml b/charts/fleet-secret/values.yaml index bac1125..03e873a 100644 --- a/charts/fleet-secret/values.yaml +++ b/charts/fleet-secret/values.yaml @@ -1,11 +1,5 @@ # Example of global values to append if we want to add more endpoints withough replciation global: - gitExternalSecrets: - useGitHubApp: true - secretStoreRefName: fleet-git-eks-secret-store - secretStoreRefKind: SecretStore - externalSecrets: {} - # ecrAuthenticationToken: # registry_endpoints: # - 1234456698772.dkr.ecr.eu-west-2.amazonaws.com @@ -23,6 +17,19 @@ gitExternalSecrets: secretStoreRefName: fleet-git-eks-secret-store secretStoreRefKind: SecretStore externalSecrets: {} + # We can overwrite above values on the cluster secret level + # Example: + # externalSecrets: + # addons: + # secretName: git-addons + # secretManagerSecretName: shared/addons + # secretType: repository + # resources: + # secretName: git-resources + # secretStoreRefName: cluster-git-eks-secret-store + # secretStoreRefKind: ClusterSecretStore + # secretManagerSecretName: shared/resources + # secretType: repository externalSecret: enabled: false From 37d9cd062de67ad1eb61769bddb4c546228d4da7 Mon Sep 17 00:00:00 2001 From: Markos Kandylis Date: Tue, 7 Oct 2025 15:41:03 +0300 Subject: [PATCH 11/11] Updated the code to all values Signed-off-by: Markos Kandylis --- charts/fleet-secret/README.MD | 143 ++++++++++-------- .../templates/gitExternalSecret.yaml | 8 +- charts/fleet-secret/values.yaml | 1 + 3 files changed, 85 insertions(+), 67 deletions(-) diff --git a/charts/fleet-secret/README.MD b/charts/fleet-secret/README.MD index 7ec5112..5522090 100644 --- a/charts/fleet-secret/README.MD +++ b/charts/fleet-secret/README.MD @@ -1,9 +1,11 @@ # Fleet Secret Helm Chart ## Overview + A Helm chart for managing Kubernetes secrets in a GitOps environment, specifically designed for cluster registration and secret management. This chart handles multiple secret types including cluster credentials, repository access, and ECR authentication, with support for both direct secret creation and external secrets management through External Secrets Operator. ## Prerequisites + - Kubernetes 1.16+ - Helm 3.x - External Secrets Operator installed in the cluster (required for external secrets functionality) @@ -13,24 +15,27 @@ A Helm chart for managing Kubernetes secrets in a GitOps environment, specifical ## Secret Types Supported ### Cluster Secrets + - Manages ArgoCD cluster secrets for registration -- Supports both direct and external secrets management -- Configurable through `externalSecret` or `secret` values +- Manages gitExternalSecrets for conecting spoke clusters to relevant repos - Automatically adds required ArgoCD secret labels ### Repository Secrets + - Handles Git repository authentication - Supports GitHub App authentication - Configurable through `gitExternalSecrets` values - Supports multiple repository configurations ### ECR Authentication + - Manages ECR authentication token rotation - Creates tokens for container registry access - Configurable through `ecrAuthenticationToken` values - Supports automatic token refresh ### AWS Secret Store + - Sets up SecretStore/ClusterSecretStore for AWS Secrets Manager - Configurable through `secretStore` values - Supports IAM role configuration for cross account @@ -41,11 +46,11 @@ A Helm chart for managing Kubernetes secrets in a GitOps environment, specifical ```yaml secretStore: - enabled: true # Enable/disable SecretStore creation - kind: "SecretStore" # Type of store - SecretStore or ClusterSecretStore - name: "aws-secrets-manager" # Name of the SecretStore resource - region: "" # AWS region where Secrets Manager is located - role: "" # Optional IAM role ARN for accessing Secrets Manager + enabled: true # Enable/disable SecretStore creation + kind: 'SecretStore' # Type of store - SecretStore or ClusterSecretStore + name: 'aws-secrets-manager' # Name of the SecretStore resource + region: '' # AWS region where Secrets Manager is located + role: '' # Optional IAM role ARN for accessing Secrets Manager ``` ### External Secret Configuration @@ -53,77 +58,77 @@ secretStore: ```yaml # Configuration for cluster registration secret via External Secrets externalSecret: - enabled: true # Enable/disable cluster registration secret - secretStoreRefName: "fleet-eks-secret-store" # Reference to SecretStore - secretStoreRefKind: "SecretStore" # Type of secret store to reference - server: "self" # Cluster API server - 'self' for local, 'remote' for external - secretManagerSecretName: "" # AWS Secrets Manager secret name containing cluster credentials - clusterName: "" # Name for the registered cluster + enabled: true # Enable/disable cluster registration secret + secretStoreRefName: 'fleet-eks-secret-store' # Reference to SecretStore + secretStoreRefKind: 'SecretStore' # Type of secret store to reference + server: 'self' # Cluster API server - 'self' for local, 'remote' for external + secretManagerSecretName: '' # AWS Secrets Manager secret name containing cluster credentials + clusterName: '' # Name for the registered cluster ``` ### Git External Secrets Configuration ```yaml gitExternalSecrets: - enabled: true # Enable/disable git repository external secrets - secretStoreRefName: "fleet-eks-secret-store" # Reference to SecretStore - secretStoreRefKind: "SecretStore" # Type of secret store to reference + enabled: true # Enable/disable git repository external secrets + secretStoreRefName: 'fleet-eks-secret-store' # Reference to SecretStore + secretStoreRefKind: 'SecretStore' # Type of secret store to reference externalSecrets: - addons: # Configuration for addons repository and external secret name - gitUrl: "" # Git repository URL - secretName: "git-addons" # K8s secret name to create - secretManagerSecretName: "" # AWS Secrets Manager secret name containing git credentials + addons: # Configuration for addons repository and external secret name + gitUrl: '' # Git repository URL + secretName: 'git-addons' # K8s secret name to create + secretManagerSecretName: '' # AWS Secrets Manager secret name containing git credentials ``` ### ECR Authentication Configuration ```yaml ecrAuthenticationToken: - enabled: true # Enable/disable ECR token generation - region: eu-west-2 # AWS region where ECR is located - name: "ecr-token" # Name of the token generator - namespace: "argocd" # Namespace where to create the secret - secretName: "argocd-ecr-credentials" # Name of the K8s secret for ECR credentials + enabled: true # Enable/disable ECR token generation + region: eu-west-2 # AWS region where ECR is located + name: 'ecr-token' # Name of the token generator + namespace: 'argocd' # Namespace where to create the secret + secretName: 'argocd-ecr-credentials' # Name of the K8s secret for ECR credentials ``` ## Parameters ### Global Parameters -| Parameter | Description | Default | -|-----------|-------------|---------| -| `secretStore.enabled` | Enable AWS Secrets Manager store | `false` | -| `secretStore.kind` | Type of secret store | `"SecretStore"` | -| `secretStore.name` | Name of the secret store | `"aws-secrets-manager"` | -| `secretStore.region` | AWS region for Secrets Manager | `""` | -| `secretStore.role` | IAM role ARN for AWS access | `""` | +| Parameter | Description | Default | +| --------------------- | -------------------------------- | ----------------------- | +| `secretStore.enabled` | Enable AWS Secrets Manager store | `false` | +| `secretStore.kind` | Type of secret store | `"SecretStore"` | +| `secretStore.name` | Name of the secret store | `"aws-secrets-manager"` | +| `secretStore.region` | AWS region for Secrets Manager | `""` | +| `secretStore.role` | IAM role ARN for AWS access | `""` | ### External Secret Parameters -| Parameter | Description | Default | -|-----------|-------------|---------| -| `externalSecret.enabled` | Enable external secret creation | `false` | -| `externalSecret.secretStoreRefName` | Reference to secret store | `"fleet-eks-secret-store"` | -| `externalSecret.server` | Server type (self/remote) | `"self"` | -| `externalSecret.clusterName` | Name of the cluster | `""` | -| `externalSecret.secretManagerSecretName` | Name of secret in AWS Secrets Manager | `""` | +| Parameter | Description | Default | +| ---------------------------------------- | ------------------------------------- | -------------------------- | +| `externalSecret.enabled` | Enable external secret creation | `false` | +| `externalSecret.secretStoreRefName` | Reference to secret store | `"fleet-eks-secret-store"` | +| `externalSecret.server` | Server type (self/remote) | `"self"` | +| `externalSecret.clusterName` | Name of the cluster | `""` | +| `externalSecret.secretManagerSecretName` | Name of secret in AWS Secrets Manager | `""` | ### ECR Authentication Parameters -| Parameter | Description | Default | -|-----------|-------------|---------| -| `ecrAuthenticationToken.enabled` | Enable ECR authentication | `false` | -| `ecrAuthenticationToken.region` | AWS region for ECR | `"eu-west-2"` | -| `ecrAuthenticationToken.namespace` | Namespace for ECR secret | `"argocd"` | -| `ecrAuthenticationToken.name` | Name of ECR token generator | `"ecr-token"` | -| `ecrAuthenticationToken.secretName` | Name of ECR secret | `"argocd-ecr-credentials"` | +| Parameter | Description | Default | +| ----------------------------------- | --------------------------- | -------------------------- | +| `ecrAuthenticationToken.enabled` | Enable ECR authentication | `false` | +| `ecrAuthenticationToken.region` | AWS region for ECR | `"eu-west-2"` | +| `ecrAuthenticationToken.namespace` | Namespace for ECR secret | `"argocd"` | +| `ecrAuthenticationToken.name` | Name of ECR token generator | `"ecr-token"` | +| `ecrAuthenticationToken.secretName` | Name of ECR secret | `"argocd-ecr-credentials"` | ### Git Secrets Parameters -| Parameter | Description | Default | -|-----------|-------------|---------| -| `gitExternalSecrets.enabled` | Enable external Git secrets | `false` | -| `gitExternalSecrets.secretStoreRefName` | Reference to secret store | `"fleet-eks-secret-store"` | +| Parameter | Description | Default | +| --------------------------------------- | --------------------------- | -------------------------- | +| `gitExternalSecrets.enabled` | Enable external Git secrets | `false` | +| `gitExternalSecrets.secretStoreRefName` | Reference to secret store | `"fleet-eks-secret-store"` | ## Usage Examples @@ -132,10 +137,10 @@ ecrAuthenticationToken: ```yaml externalSecret: enabled: true - secretStoreRefName: "fleet-eks-secret-store" - server: "remote" - clusterName: "prod-cluster-01" - secretManagerSecretName: "cluster-prod-01" + secretStoreRefName: 'fleet-eks-secret-store' + server: 'remote' + clusterName: 'prod-cluster-01' + secretManagerSecretName: 'cluster-prod-01' ``` ### ECR Authentication Setup @@ -144,8 +149,8 @@ externalSecret: ecrAuthenticationToken: enabled: true region: eu-west-2 - namespace: "argocd" - secretName: "ecr-creds" + namespace: 'argocd' + secretName: 'ecr-creds' ``` ### Git Repository Authentication @@ -153,17 +158,26 @@ ecrAuthenticationToken: ```yaml gitExternalSecrets: enabled: true - secretStoreRefName: "fleet-eks-secret-store" + secretStoreRefName: 'fleet-eks-secret-store' + secretStoreRefKind: 'SecretStore' + useGitHubApp: true externalSecrets: addons: - secretName: "git-addons" - secretManagerSecretName: "git-addons-creds" + secretName: 'git-addons' + secretManagerSecretName: 'git-addons-creds' + resources: + secretName: 'git-resources' + secretManagerSecretName: 'git-resources-creds' + secretStoreRefName: 'cluster-git-eks-secret-store' + secretStoreRefKind: 'ClusterSecretStore' + usePrivateKey: true ``` -Values defined under `global.gitExternalSecrets.externalSecrets` act as shared defaults and -are deep-merged with the chart-level `gitExternalSecrets.externalSecrets`. Define the common -portions (for example `secretName`, `secretType`, or annotations) globally and only override -the differing keys per cluster or environment. +- `secretStoreRefName`, `secretStoreRefKind`, and authentication flags (`useGitHubApp`, `useHttp`, + `usePrivateKey`) can be supplied globally under `gitExternalSecrets` and overridden per secret + by setting the same keys inside each `externalSecrets` entry. +- Per-secret overrides merge shallowly: specify only the fields you need to change for that + repository while inheriting the rest from the top-level configuration. ## Notes @@ -183,4 +197,7 @@ the differing keys per cluster or environment. ## License This chart is licensed under the Apache License 2.0. + +``` + ``` diff --git a/charts/fleet-secret/templates/gitExternalSecret.yaml b/charts/fleet-secret/templates/gitExternalSecret.yaml index 4f553e3..8a08251 100644 --- a/charts/fleet-secret/templates/gitExternalSecret.yaml +++ b/charts/fleet-secret/templates/gitExternalSecret.yaml @@ -1,9 +1,9 @@ {{- if .Values.gitExternalSecrets.enabled }} {{- $secretStoreRefName := .Values.gitExternalSecrets.secretStoreRefName | default "" -}} -{{- $secretStoreRefKind := .Values.global.gitExternalSecrets.secretStoreRefKind | default "" -}} -{{- $useHttp := .Values.global.gitExternalSecrets.useHttp | default false -}} -{{- $useGitHubApp := .Values.global.gitExternalSecrets.useGitHubApp | default false -}} -{{- $usePrivateKey := .Values.global.gitExternalSecrets.usePrivateKey | default false -}} +{{- $secretStoreRefKind := .Values.gitExternalSecrets.secretStoreRefKind | default "" -}} +{{- $useHttp := .Values.gitExternalSecrets.useHttp | default false -}} +{{- $useGitHubApp := .Values.gitExternalSecrets.useGitHubApp | default false -}} +{{- $usePrivateKey := .Values.gitExternalSecrets.usePrivateKey | default false -}} {{- $clusterGitSecrets := .Values.gitExternalSecrets.externalSecrets | default dict }} {{- range $externalSecretName, $externalSecret := $clusterGitSecrets }} diff --git a/charts/fleet-secret/values.yaml b/charts/fleet-secret/values.yaml index 03e873a..11718ac 100644 --- a/charts/fleet-secret/values.yaml +++ b/charts/fleet-secret/values.yaml @@ -25,6 +25,7 @@ gitExternalSecrets: # secretManagerSecretName: shared/addons # secretType: repository # resources: + # useHttp: true # secretName: git-resources # secretStoreRefName: cluster-git-eks-secret-store # secretStoreRefKind: ClusterSecretStore