From 774a1ba0a8540b61878327bb73d020988cf471a9 Mon Sep 17 00:00:00 2001 From: Nicolas Bigler Date: Tue, 24 Jan 2023 18:09:36 +0100 Subject: [PATCH 1/3] Fix connection secrets for VshnPostgreSQL Setting the parameter `writeConnectionSecretToRef` on the claim should result in a secret with the connection credentials named after the value set in the `name` key of that parameter. However, the current implementation writes the connection credentials to a hardcoded secret called `-connection`. This leads to confusions for the end user. This commit will fix this issue, by creating a temporary secret in the namespace where the instance is placed and will then copy the content to the secret specified in the claim. Signed-off-by: Nicolas Bigler --- component/vshn_postgres.jsonnet | 9 +++- .../appcat/21_composition_vshn_postgres.yaml | 45 ++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/component/vshn_postgres.jsonnet b/component/vshn_postgres.jsonnet index 4694a3f69..aec41247c 100644 --- a/component/vshn_postgres.jsonnet +++ b/component/vshn_postgres.jsonnet @@ -172,6 +172,10 @@ local composition = toFieldPath: 'data.POSTGRESQL_PASSWORD', }, ], + writeConnectionSecretToRef: { + name: '', + namespace: '', + }, }, }; @@ -283,16 +287,19 @@ local composition = }, { base: secret, + connectionDetails: comp.conn.AllFromSecretKeys(connectionSecretKeys), patches: [ comp.PatchSetRef('annotations'), comp.PatchSetRef('labels'), comp.FromCompositeFieldPathWithTransformSuffix('metadata.labels[crossplane.io/composite]', 'metadata.name', 'connection'), - comp.FromCompositeFieldPath('metadata.labels[crossplane.io/claim-namespace]', 'spec.forProvider.manifest.metadata.namespace'), + comp.FromCompositeFieldPathWithTransformPrefix('metadata.labels[crossplane.io/composite]', 'spec.forProvider.manifest.metadata.namespace', 'vshn-postgresql'), comp.FromCompositeFieldPathWithTransformSuffix('metadata.labels[crossplane.io/claim-name]', 'spec.forProvider.manifest.metadata.name', 'connection'), comp.CombineCompositeFromTwoFieldPaths('metadata.labels[crossplane.io/composite]', 'metadata.labels[crossplane.io/composite]', 'spec.forProvider.manifest.stringData.POSTGRESQL_HOST', '%s.vshn-postgresql-%s.svc.cluster.local'), comp.FromCompositeFieldPathWithTransformPrefix('metadata.labels[crossplane.io/composite]', 'spec.references[0].patchesFrom.namespace', 'vshn-postgresql'), comp.FromCompositeFieldPath('metadata.labels[crossplane.io/composite]', 'spec.references[0].patchesFrom.name'), + comp.FromCompositeFieldPathWithTransformPrefix('metadata.labels[crossplane.io/composite]', 'spec.writeConnectionSecretToRef.namespace', 'vshn-postgresql'), + comp.FromCompositeFieldPathWithTransformSuffix('metadata.labels[crossplane.io/claim-name]', 'spec.writeConnectionSecretToRef.name', 'connection'), ], }, ] + if pgParams.enableNetworkPolicy == true then [ diff --git a/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml b/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml index 2b3a1e1ea..6b4090e0f 100644 --- a/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml +++ b/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml @@ -285,6 +285,31 @@ spec: name: '' namespace: '' toFieldPath: data.POSTGRESQL_PASSWORD + writeConnectionSecretToRef: + name: '' + namespace: '' + connectionDetails: + - fromConnectionSecretKey: POSTGRESQL_URL + name: POSTGRESQL_URL + type: FromConnectionSecretKey + - fromConnectionSecretKey: POSTGRESQL_DB + name: POSTGRESQL_DB + type: FromConnectionSecretKey + - fromConnectionSecretKey: POSTGRESQL_HOST + name: POSTGRESQL_HOST + type: FromConnectionSecretKey + - fromConnectionSecretKey: POSTGRESQL_PORT + name: POSTGRESQL_PORT + type: FromConnectionSecretKey + - fromConnectionSecretKey: POSTGRESQL_USER + name: POSTGRESQL_USER + type: FromConnectionSecretKey + - fromConnectionSecretKey: POSTGRESQL_PASSWORD + name: POSTGRESQL_PASSWORD + type: FromConnectionSecretKey + - fromConnectionSecretKey: ca.crt + name: ca.crt + type: FromConnectionSecretKey patches: - patchSetName: annotations type: PatchSet @@ -297,8 +322,12 @@ spec: fmt: '%s-connection' type: string type: FromCompositeFieldPath - - fromFieldPath: metadata.labels[crossplane.io/claim-namespace] + - fromFieldPath: metadata.labels[crossplane.io/composite] toFieldPath: spec.forProvider.manifest.metadata.namespace + transforms: + - string: + fmt: vshn-postgresql-%s + type: string type: FromCompositeFieldPath - fromFieldPath: metadata.labels[crossplane.io/claim-name] toFieldPath: spec.forProvider.manifest.metadata.name @@ -326,6 +355,20 @@ spec: - fromFieldPath: metadata.labels[crossplane.io/composite] toFieldPath: spec.references[0].patchesFrom.name type: FromCompositeFieldPath + - fromFieldPath: metadata.labels[crossplane.io/composite] + toFieldPath: spec.writeConnectionSecretToRef.namespace + transforms: + - string: + fmt: vshn-postgresql-%s + type: string + type: FromCompositeFieldPath + - fromFieldPath: metadata.labels[crossplane.io/claim-name] + toFieldPath: spec.writeConnectionSecretToRef.name + transforms: + - string: + fmt: '%s-connection' + type: string + type: FromCompositeFieldPath - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object From ea4f47630bd3a211689a37dd4d4653701b80b1be Mon Sep 17 00:00:00 2001 From: Nicolas Bigler Date: Thu, 26 Jan 2023 12:03:11 +0100 Subject: [PATCH 2/3] Fix ArgoCD sync loop Kubernetes will remove empty key-value pairs in the metadata. This results in an infinite sync loop, if empty values are present in the metadata. We therefore remove those empty values to avoid those loops Signed-off-by: Nicolas Bigler --- component/vshn_postgres.jsonnet | 25 +++-------- lib/appcat-compositions.libsonnet | 4 +- .../appcat/21_composition_vshn_postgres.yaml | 41 ++++++------------- 3 files changed, 18 insertions(+), 52 deletions(-) diff --git a/component/vshn_postgres.jsonnet b/component/vshn_postgres.jsonnet index aec41247c..518556f15 100644 --- a/component/vshn_postgres.jsonnet +++ b/component/vshn_postgres.jsonnet @@ -61,10 +61,7 @@ local composition = spec+: { forProvider+: { manifest+: { - metadata: { - name: '', - namespace: '', - }, + metadata: {}, spec: { cpu: '', memory: '', @@ -101,10 +98,7 @@ local composition = spec+: { forProvider+: { manifest+: { - metadata: { - name: '', - namespace: '', - }, + metadata: {}, spec: { postgresVersion: '', 'postgresql.conf': {}, @@ -119,10 +113,7 @@ local composition = spec+: { forProvider+: { manifest+: { - metadata: { - name: '', - namespace: '', - }, + metadata: {}, spec: { instances: 1, sgInstanceProfile: '', @@ -148,10 +139,7 @@ local composition = spec+: { forProvider+: { manifest+: { - metadata: { - name: '', - namespace: '', - }, + metadata: {}, stringData: { POSTGRESQL_USER: defaultUser, POSTGRESQL_PORT: defaultPort, @@ -184,10 +172,7 @@ local composition = spec+: { forProvider+: { manifest+: { - metadata: { - name: '', - namespace: '', - }, + metadata: {}, spec: { policyTypes: [ 'Ingress', diff --git a/lib/appcat-compositions.libsonnet b/lib/appcat-compositions.libsonnet index 23690829d..1848b7366 100644 --- a/lib/appcat-compositions.libsonnet +++ b/lib/appcat-compositions.libsonnet @@ -139,9 +139,7 @@ local connFromFieldPath(name, field) = { local kubeObject(apiVersion, kind) = { apiVersion: 'kubernetes.crossplane.io/v1alpha1', kind: 'Object', - metadata: { - name: '', - }, + metadata: {}, spec: { providerConfigRef: { name: 'kubernetes', diff --git a/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml b/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml index 6b4090e0f..4de2a48ee 100644 --- a/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml +++ b/tests/golden/vshn/appcat/appcat/21_composition_vshn_postgres.yaml @@ -26,8 +26,7 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: @@ -58,8 +57,7 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: @@ -93,16 +91,13 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: apiVersion: stackgres.io/v1 kind: SGInstanceProfile - metadata: - name: '' - namespace: '' + metadata: {} spec: cpu: '' initContainers: @@ -155,16 +150,13 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: apiVersion: stackgres.io/v1 kind: SGPostgresConfig - metadata: - name: '' - namespace: '' + metadata: {} spec: postgresVersion: '' postgresql.conf: {} @@ -201,16 +193,13 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: apiVersion: stackgres.io/v1 kind: SGCluster - metadata: - name: '' - namespace: '' + metadata: {} spec: configurations: sgPostgresConfig: '' @@ -260,16 +249,13 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: apiVersion: v1 kind: Secret - metadata: - name: '' - namespace: '' + metadata: {} stringData: POSTGRESQL_DB: postgres POSTGRESQL_HOST: '' @@ -372,16 +358,13 @@ spec: - base: apiVersion: kubernetes.crossplane.io/v1alpha1 kind: Object - metadata: - name: '' + metadata: {} spec: forProvider: manifest: apiVersion: networking.k8s.io/v1 kind: NetworkPolicy - metadata: - name: '' - namespace: '' + metadata: {} spec: ingress: - from: From 8aaeccefe25bcd7acecddd8ee2b74bc63b96d3f7 Mon Sep 17 00:00:00 2001 From: Nicolas Bigler Date: Thu, 26 Jan 2023 15:28:41 +0100 Subject: [PATCH 3/3] Document how to handle secrets not coming from a crossplane provider Signed-off-by: Nicolas Bigler --- component/vshn_postgres.jsonnet | 1 + .../crossplane-secrets-non-provider.adoc | 186 ++++++++++++++++++ .../pages/explanations/crossplane-secrets.png | Bin 0 -> 23245 bytes docs/modules/ROOT/partials/nav.adoc | 3 + 4 files changed, 190 insertions(+) create mode 100644 docs/modules/ROOT/pages/explanations/crossplane-secrets-non-provider.adoc create mode 100644 docs/modules/ROOT/pages/explanations/crossplane-secrets.png diff --git a/component/vshn_postgres.jsonnet b/component/vshn_postgres.jsonnet index 518556f15..5aae1030a 100644 --- a/component/vshn_postgres.jsonnet +++ b/component/vshn_postgres.jsonnet @@ -160,6 +160,7 @@ local composition = toFieldPath: 'data.POSTGRESQL_PASSWORD', }, ], + // Make crossplane aware of the connection secret we are creating in this object writeConnectionSecretToRef: { name: '', namespace: '', diff --git a/docs/modules/ROOT/pages/explanations/crossplane-secrets-non-provider.adoc b/docs/modules/ROOT/pages/explanations/crossplane-secrets-non-provider.adoc new file mode 100644 index 000000000..8fef28bce --- /dev/null +++ b/docs/modules/ROOT/pages/explanations/crossplane-secrets-non-provider.adoc @@ -0,0 +1,186 @@ += Manage secrets not coming from a provider + +An operator or a helm chart can't create a crossplane connection secret. Therefore we need to create one. + +To be able to manage the connection secrets via crossplane, we create a helper secret object via provider-kubernetes. +The helper secret object is populated via composition and is made aware to crossplane via `writeConnectionSecretToRef`. +Doing this we simulate the behaviour of connection secrets from a crossplane provider. + +Once we've got a crossplane managed secret, we can use `writeConnectionSecretToRef` in the claim to reference the final secret (which is automatically created by crossplane) + +Visualizing this process it looks like the following: + +image::crossplane-secrets.png[] + +== Example + +Let's assume we've got an operator or a helm chart that creates a secret called `` in the namespace `vshn-` that contains a password in the key `superuser-password`. + +We will then create a secret object in our composition like shown in the snippet below. + +This will result in a regular temporary secret (our helper secret) which will also be placed in the `vshn-` namespace. The key `MY_PASSWORD` is patched in from the secret created by the operator or helm chart. (1) + +The name and namespace of that secret is also patched in accordingly. (2) + +By using the `writeConnectionSecretToRef` key, we make crossplane aware of the connection secret. (3) + +We again patch the name and namespace of that helper secret object accordingly. (4) + +Finally we list the propagation secret keys from that object to the composition instance connection secret. (5) + + +[source,yaml] +---- +- base: + apiVersion: kubernetes.crossplane.io/v1alpha1 + kind: Object + metadata: {} + spec: + forProvider: + manifest: + apiVersion: v1 + kind: Secret + metadata: {} + stringData: + MY_APP_PORT: 1234 + providerConfigRef: + name: kubernetes + references: + - patchesFrom: # (1) + apiVersion: v1 # (1) + fieldPath: data.superuser-password # (1) + kind: Secret # (1) + name: "" # (1) + namespace: "" # (1) + toFieldPath: data.MY_PASSWORD # (1) + writeConnectionSecretToRef: # (3) + name: "" # (3) + namespace: "" # (3) + connectionDetails: + - fromConnectionSecretKey: MY_APP_PORT # (5) + name: MY_APP_PORT # (5) + type: FromConnectionSecretKey # (5) + - fromConnectionSecretKey: MY_PASSWORD # (5) + name: MY_PASSWORD # (5) + type: FromConnectionSecretKey # (5) + patches: + - patchSetName: annotations + type: PatchSet + - patchSetName: labels + type: PatchSet + - fromFieldPath: metadata.labels[crossplane.io/composite] # (4) + toFieldPath: metadata.name # (4) + transforms: # (4) + - string: # (4) + fmt: '%s-connection' # (4) + type: Format # (4) + type: string # (4) + type: FromCompositeFieldPath # (4) + - fromFieldPath: metadata.labels[crossplane.io/composite] # (4) + toFieldPath: spec.forProvider.manifest.metadata.namespace # (4) + transforms: # (4) + - string: # (4) + fmt: vshn-%s # (4) + type: Format # (4) + type: string # (4) + type: FromCompositeFieldPath # (4) + - fromFieldPath: metadata.labels[crossplane.io/claim-name] # (4) + toFieldPath: spec.forProvider.manifest.metadata.name # (4) + transforms: # (4) + - string: # (4) + fmt: '%s-connection' # (4) + type: Format # (4) + type: string # (4) + type: FromCompositeFieldPath # (4) + - fromFieldPath: metadata.labels[crossplane.io/composite] # (2) + toFieldPath: spec.references[0].patchesFrom.namespace # (2) + transforms: # (2) + - string: # (2) + fmt: vshn-%s # (2) + type: Format # (2) + type: string # (2) + type: FromCompositeFieldPath # (2) + - fromFieldPath: metadata.labels[crossplane.io/composite] # (2) + toFieldPath: spec.references[0].patchesFrom.name # (2) + type: FromCompositeFieldPath # (2) + + - fromFieldPath: metadata.labels[crossplane.io/composite] # (4) + toFieldPath: spec.writeConnectionSecretToRef.namespace # (4) + transforms: # (4) + - string: # (4) + fmt: vshn-%s # (4) + type: Format # (4) + type: string # (4) + type: FromCompositeFieldPath # (4) + - fromFieldPath: metadata.labels[crossplane.io/claim-name] # (4) + toFieldPath: spec.writeConnectionSecretToRef.name # (4) + transforms: # (4) + - string: # (4) + fmt: '%s-connection' # (4) + type: Format # (4) + type: string # (4) + type: FromCompositeFieldPath # (4) + +---- + + +Assuming we've got a claim called `my-great-app` with the following content: + +[source,yaml] +--- +[...] +metadata: + name: my-app + namespace: my-app-namespace +spec: + writeConnectionSecretToRef: + name: my_connection_secret +--- + +The operator or helm chart will thus create a secret like this: + +[source,yaml] +--- +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: my-app-c48ad + namespace: vshn-my-app-c48ad +data: + MY_PASSWORD: Rm9vYmFy +--- + +The composition will create the helper secret like this: +[source,yaml] +--- +apiVersion: v1 +kind: Secret +type: connection.crossplane.io/v1alpha1 +metadata: + name: my-app-c48ad-connection + namespace: vshn-my-app-c48ad + ownerReferences: + - apiVersion: kubernetes.crossplane.io/v1alpha1 + controller: yes + kind: Object + name: my-app-c48ad-connection + uid: f22e3083-6a89-4286-ba6d-f990ee91ccad +data: + MY_PASSWORD: Rm9vYmFy + MY_APP_PORT: 1234 +--- + +Finally the claim secret will be populated like the following: +[source,yaml] +--- +apiVersion: v1 +kind: Secret +type: connection.crossplane.io/v1alpha1 +metadata: + name: my_connection_secret + namespace: my-app-namespace +data: + MY_PASSWORD: Rm9vYmFy + MY_APP_PORT: 1234 +--- diff --git a/docs/modules/ROOT/pages/explanations/crossplane-secrets.png b/docs/modules/ROOT/pages/explanations/crossplane-secrets.png new file mode 100644 index 0000000000000000000000000000000000000000..e3ff72f9b1353dd9ba53e78b7518a7e91982661c GIT binary patch literal 23245 zcmeEuS5#C@)2<>S8HO-K2?HqTC@^G1@(_nCI3SA53_0fccs zQ6vchiYQqmr_&pJ-|wt-ZvLBdan|{6T)1cN?&|95>Z3BnZO*(2$>LW*v&}nO_ z8y`7BMm%!lXe=cKcry5sefh|d<3`?SthbB5t%I}85g`e+!@q=3V)kxc-a-=ULMW8A zyZd>9gSDNfwTstzR~v8e2>kBiMsTooupu12gAzkYoD&m2hmyV|E-56Tf|3INNTSZm zN=jKAzHe=30b#7TxX9seHs02DhwY&4tyTT~RehwqjT|v!Y{r%A4 zukh-Da@s1^fwFEkZr;u~l#IWwyYFEmQf35wyp)$8+E7e95M!c6&s#wT3<}P}|E*!$R9k#okd$jv%3m*EN^a@)JYL+Gt@l15qYs?w4hRP^!*4ehylJ z1TB=kwT=Zoz*tRE(oaUs+f?teqmP$?j+C^Ptsl$-SJiR-v}s~R~wdKnpD4Yjdo7k6oC zf|#KZ!3U-3E$M-`AsEW3Nn=a{RK@gUE$|pKU1x7R7Au9j1jeJGBW>)VZz~Pj_Hi@R zaW%bcW^Jk=fm3tz@eT}B$74`9C#atLVLJkHMI$Kb^UBDG&Loqj4iM_cso;XeRn&JKp#6(yn&Cg1i?#EM%zr>U(N!f?_?(Ksb}YH z8&mfAN0Knt|eGcD8DE8osKM&S(Q|7jv|MJ3&pyQP;!{ zykRY;Vc{%ef(jJJyX%Shn&ISBrOhSqVwYVUEp&`AI;QSEwggu{GYK6}J27cH9Sxup zUxL3ER@2z^vbVdJx|THFRw@9kts$%DAnO8JM45WnJBn$8za)TpYhblKBsEL|&2Tmb zX8r_M6vj}?+t1Hb+*lV2nzD6rmen`*0`J*5o1wh?%vJ16rDXzr?N$6G4P6{{ja|ef zH5?sf-SNJGI@UVUSW`DsTSxGrq>d*vJ31x=*-I#YeSdL(!%Ifu&JF|(e~h=9w4{uM zqn)IVg`X?RNeA!bkMU6#$GEA;`I%mpB}nS1U-GrHb#ZXAwsyeTNt?Rb%UE32F>z7x zvUXFKleBd;azq=-x$6M2?0uz;E}3Xc_!{V%`q)ZpxVu<5Ne1Y-c)8nIdtUZ*($SKV zaK%emIO)mRt9$#JV2vgHWsF_LF8g>G+3DGu>$*5;5M(9P@CLdBRRY1?4Pz>)YN~Bu z=49b!?_i^A3i|Li5_4CR)tB~{)Wu5>G@PCBo<^=tPA&xX%f4cGKMRbhpO1v@r2rdG z*#Hl$fwz<@7`nNMq_nHLxRbW7X~1O%EoffUB|POWOJQ)%D1x?}m%X~W1J=-6&DJnL zTH4(a>y6UTlCbx+y)36|0Cnm_kdo7~aq`Eh%c*$CSQBI<>;vTNWFc=O0ol$!9_GN^ zfdBvXo)Tzk@8WbvWm z?ySkDPj~6|i^+-Pf_z7*w3Nx1Qp3r&_{DN?eIES(J`gliS0>F2f}j3*OIWZ7>+Syc zedwVu{Nyp{+rKZUoxX1CG1H@4D<|@^kC%ObuxC3>q@<=!S9*?Q0cnlRrNL z@8onUTl!gA=-Yg|PWV3q=w*ty)jkP+gH7i+J#pXqSsI(7v?p6u_4>@jAU=f-pD3;& zyWGay`sxHP_gL8eZsoUh-59U;7iG#C*M=uvpRJ(v>JiuU{~B?k1$50RJ>p#LJ|40j zE#Ap3;avO4%o_ZM4m9b%6Ef^jN+&bskKb6UZ$3#c_bs*Zqp@!Ed!>Dax!TXyCVC{^ z(@rHy==1eIdn-NCmfSDR@xD)k>s(pW)~|_PHHM@x*Ylkx{Ve^zrD?X@U{GYg7QEbE z*{e!(JaoH5rU`b?X`#Tdr9vUu}8l$H_3W8eNm<_n; zm3Eev`V03YdA2hvf`u$SyM-oN8I)+>oU6Aa$n0%QG1$M~{87leHWswl#kQb%(&rMp z->3)8h?@h!KmFf{+=x`Zf?K{5%R}uq9^$t#zQ4y7Er4@;C-;N4-#{sZ1=Ej_(8Kx; z*|c_Wxp7AudD(ho=t;+iDQ(T%z3ZOqHx|$ebk8;xI9o0(3{xI)ec>WIQ*@(c^|KZ4 z!qcf__Yc~qqpRO5Ua<7}WIFNvwiP{%XoLIH@3*c@%3X0Lp`XszjkH8D>$96-t)nY{ zzdjp#(r-iY0;adlvV)X75=wD8PsW3Ku=&T8{y+6d#V z+g70gHQTx4JGNM-^4gZN>UW&U0X2`DTiMy zJU_daoMfQ5N$Amm58dEXM0Xt5D1dR%d_ zY1>u#BfK`nzwTdG5n)DIBLu&E#idrZ5o2zb?}LjWj%S(KaI#0NPx0={CWxc#-r~}J z+M(P)ml{19kn)V$_~8$yfNdwND(?R2Ctdrcng=ZlapI6AV;Wdm!FdQ*7V=r2&sSwW zcwppA7)%;jP??0i%8IUn80@#FA|+XfQSW{|lAFw^yIR?eL_t-yzSok!X(f8ZjB_!a zqIH8f>-sOg#JaHES*w&GZPcAOH2K)AE3O>xRsQHHZho6+-rse*bCGaz0zFXsEHxkL z7@glP^`m?P6|)Q%wLx{}4eZ`D?WoO?p&3@64_e(@W>_~bj@ zgsCOICL7TCS9%1VqoKY&bcyvNss9f=E9Wh@8Nv+P%uPS~(53|SM6N2`xhoz7bl zB4G*dueVP|(wt{^@t@AN)2Qhl?&7e^6W5o+pXSh1KRHVaYu%o!Hpn8RYZq_aQ`lK* z^`Oq5BF@RpmQ^F}cF1AY1#V_P7jv0+Rz^g&PNi{x6N zPco&_oJ!BKF?HCF`)sH@!b$ViE*6<=!(;)qZf~Eoiw%13BzNZP$9bx6Qoc96d{xRB z*md;0M7~?vzne`tPEUB6$96049%D&C;4fiEtY+L(E@@}1<#y*A zwm4m|a%~xm%mv{vod$)-E8@iKb&^ybjG&uG<-QH6Bihb(L?TNppbKQ7K?*uUC-L_#&BiMjN^(`y@ zWEYSB?@0zXH39Byv(gImd_77`LG$0uO8L4L&WMmN6pkfq7ZmRA{KzjK9R6o-O;wJi z6oy|QugY{}A(k7iaR0L#KG#+z4eAYt3{TQIUvsyf^7xMfd4M?Dj$>^s*86u4E<%vA z5LrE5gMVPTr~|Jj27?>3~d-I6@PNvEJsGQS9kbe{)XlO*1(4HW#QT^}t%B(TA6e|83~6;{M;zk+KLbP3Ig_s`55@3-n(WOZaY+x>jSDdNvoG($>w2NLR~eQcS7xE>$f&BL?J_X#HGmy z9H|V3Yh<#|P(GtlqDNbM;cD}zcQ%~Jh!2$am>5`y_vv6~Zz%_n8&jd?jUJ2-ZIy>A z_T#8-qWLoYi?B$iC~O7!uwbfAssppyy#8mmTCZ#h-6Bb{hi%8&aSm#22|~R*5tkw{ zK`g)g>OvnEzOTWO*#J>^gyC;E`3KHMNK@ToA`G_~zUW`QGH+S(ib*(0`OGp{vBQic z994}bUnXBaPxMxP`G1G}-&y{D!LS8S!}mAehs9X!{<=J|HsZ#xzqj7ps<6_-yau8M zC{49+ckfF?tpJ;YIW8{%*huPo-TbfF^C2+a9w#INJiEx7oN|?$onDBs^<>)pH|v{sUG8;!25~C8#Iw^Z znX$Z~vd#_59TOl=oNJm8s`Urq>G6frZ`V`ObE<}o7{T%t6oSJN$YYYEhlQP=eZOVh zLUo$oc2Pyfk&Z3=-OTNu$@# zfSWV^dg-b0?m)5UGO)hRXp?#oJxgzp=NvB%$OL5I?O z-1hiJhYW=f+zsS#m0x^Ej;o`53f)_%tS947)4$Dbhw@>?tGGAh%oxUGx#=wTUk^)T zKq4Yj_lnuxYGNCtt(xMRF)SXjdX%mx;`*qUf&c#Q`qbviTN$gJc_?jrx;dTO+;)tj zayWE*zO|Tmp7RNa#k-=*X1`?^@Vt|sE!zmX+q+5?_~(2B9w?F%o(4Hyx<+$dMQLA2UI1b;tk6~7+OptK`~tRMB902w;% zREkpQ#O@up#25Q}+nQwVKl1(7xJ)RXkLQK$ZCWojt|yaxGfmB?pw#@j?L)a+AN0dK z(%3axqFRvjKiv1_LG1yzQ|o;jY$bdTdnQ48%XJXke~qLGb&ylY17#P72Y%1ITzo_gYs|~W)d{WPlwv1ffecXc!IEJ)c;ckD zVP3JzJ0wC7mJ+trU)VD0_5S4X&nI|;8Kb#y)4cl|nl(3`3p-Ub`j!zB}n+&ud7;ZC7`!-ZGFHHllL{3-U3Hq4VGIHnSZy?ZSHus$Tg6B(_g;o zW9q;8{Wg9#j&zWlE{%e9ZVhsLnzLnl){APdGm(i9t099%P4=f zqdMAS@06~6N@VVAKgL8)%2f7GuiD*3kO+BcE|L-T7nRQEZdwDC%7zF~bVDB6SMW}CLn#L(Pk ztm_9N0EToJk#PU%Pl4Zz3?VcvDT%Pow5w`jMp~aTzHfm_4re<*&IKw@?1LfaqOG-x zl#z(6tm%^8cfj+VER^n!ng?|YsMf=!jZAk52EGJ;DkU;*bD+M7^)vV0pHihH4l#0; z?@+zw^R*zCU?%6dc3}8U_sYhXCR*{la)r*qb?H(rgKA%(lzykaBL|HlV%(U8W#Rnl zS6HgbLA9VKuC$jzf{fL?pY^h(_@4TJ`}3bA#I<}-B_XA-$Re7|Td{4&NLnFopzspc z#1fB=oH~{3%v2pfKCaPG{h;Ebw%P4{=IbJZof)!2A8nf|MSi;<63_HcO~kfoTLx3gR0V{* z0d9Xj%Up1~Gef}qfriSFba8b{zDGHwHZUHjV#Gpxudsqq4mQ$P+WnLDR@FZBzS6TfdI3mi#ZTcR>iUi1_(Bqa_o~PWG!*o_?M9~V2#6KAh+ZrNF#5ijd zk^B{?ke#*-hy5bo-rL&hRN50Cd3v_#jq5IVD$-5dtvrN(>L9CI_3nev|<_TvnE zfJ>{rU1n|x%7}O6C^bu#yE~+t>Ao-$F{Lcad@uV9$ZYQdE0aZxE}fX?D|pD|y1Tj> zG`@W;c-ZzeXS0bvr{+#Nzx}jsKDx;z4&6bvhPr6bk@eGmLz?D`YfK>s&1JGp@0(A&&4n-^59ZxADB5*oJA#J{s0j(4Z5@mO$`J0~Q z1N&im{OOFCGcLccS4592W=5 zm+p0mx5m&Bo#0%!n82(@r*24NNm@&)$UcTPx*>z%Qvw4|u;1=;4V*QWAWssKWjpgASthh?ea ze72aJc0_h0VjKZC*L}UuMe~Z|>;_dIrf31JE!6cV@~Y^_N4GDfO6(*o%dw`t)ru5B zxcP~9AMzbavQIs^YD-1m|NL0Kl^}emsYJMh(f z7RG7RB<{=j<~zShmKXP5K&hN|JUZqGBBfMoOc{TMng7*DS3>|s1&Nqrq@a{P&u6@b zF{;uh_h_LJ*|}iCmh>d{_+c2|xF=Fn zmVX%5mB>4wl78-fNN^u9T_^SD@fX+X9*#dZ2ouARgem@naOL>7BB^n-5gNr2Qhf(n z;#bL=6)mA<$#{Btd6G{YHlYPiTU`tp5lJAn8J2Wt6P60y)cnFQ(iRvHcb(ZoEZBX`Z2dmPERdeDzP-s-aN`%0}MW@z$;;@kCQ$K?fk>7Yd1wepuQ zSJ?&It7oHSmbr@4YG2G1y&MO5emHn{DgoVDMSeynoY-%Cm29q4$CqTZA$dyFd$~9p zM|vLGkD~I<2*-IG8MbK&SbBA0sy=rpwI(CF@y~{*7XHUx@$*eT6m>(NQ#bL*w%qS{ zwBpgO^{N1`0-2IGXKb!%+6?9CHik$L<1|X{b|Vp-Cd|ZOt?&wY(i`&awyJ#i3t8IL z7eC!6=daa~CW+W!i_GzjN#lB)(~2u>F6t!@(PzsR#Y^{9&rW(_dgN2v9dP>4GI2MTe0*on@m+Iq>x@YG?T_K#hd*Ajt2uT3S}?Ncce;g>u`!kp{1>OsbM zXVFi>4>yzMuM9>e?p_Mpd}Gy7UN%^qu=u(^@DN0KtL}4FTRj z^e^qW6-~OY-D@s3!8zWQIMOB!7X6uagq)-)I`UcsKFa}f5`quuri4&$_Hae-k|?hS z$E2uq-8SCXxDdoyB_|Q&Mr!^Df?}D;3@`5Y*P@{Ft?Mv}!rOQQbwyZLeQrPtA?W1V zXS|oAbYFo>-4HG}cp#eEpwn|T8+`qq(985IXjAFGq z^7XwKUu88FvS%=*(#XEB&c8aNrT?M}sG3vewU&IGQ`4T##E^CNUq&^=`J(7C%|rUeqTc#uPoo~HuviSqknA6p^mhkvxU7)#i~9p@MQ z)}FQT1WSWcuTH(sun=dT$hQscI#wGBd{f~DY?CI^)^-x>dvi*^9^A#{j`9u7tw!U6jHGm~&zNd0=fU)x@xiQuEU&d8yOgNh*$k(;<-W*i z_EwDE_-wY`T6YYoPbo5X@WaMO;UUzdtw7yx^w~+SLRhqvHrXVXc$%}I$L-i_DCaqm zu2aJyx<<6TiC94mzQpcOPlE~WEpun-=-`r{^KrlutQ8pvICJ8A)dkh zzSMbBW^OLm9QnNS&*by-kKbOJR4E5x*73fU>w+&jV}>cEjF-t>&Z|?LA6F=mlt$U$ zO;NGr0u)kgsS zOTVO;BM*y|j=UYZJ7=dvWT2=aZj>0jU6VMq@r^I^+lwh@rgx{0ap7Qh;#{1cU(*1a z;mubiw|zXz`GXU597FL_!P36Auj?NCNfJkY_NulodNSRE^KwQb)1{`K3k4C~zgc`g zT4<2N^n=l^%~El5=rWR?rS*gW{MkZjj^iVC-Hq1G`@jCd0>~a+r@=`tKWh%6S*Mu; z8`4WovsXT+6DxT1SU8olHeVSYB~L_oiFzRP>PGq_Y)C){vGr6D#>HM2fo#W=4_a%X&`5-3VwI_S8rc(OKe;Tix1x z7x?1{>B775?ZrWu%!lH^yHs>GRnezT9L4Q8S?F4j`;4WfjNlt#wk%gSkgfq#FUB-@{>h}^Nb;Vm-%n4KO zuE-%#g32kqLyE0q{62_gBqU`0ygRPe_1mn2#tIbz^@qs$KfC{YBy^^uVn=k4qew|<8r&FEyIPAZ3~lF9b^$DNKh zNCmOAx5(cw^)XZ9xp>+=2E$VXx$*llTW!DKWvt)K&laK9efZQsQqgAE3i-W$Z^12Zyb$|sz4)odRrGq} z^d4*b1=J(7c;U8sHI_h;WX_8dq+MnsHE1E%{8Fr+E8R~&oU;U&hsv*9saQ(EDt(r! zXma5;@<=L`IBfA!TU09E;#T;LxFV3SsPj+KPpH^Zsh|yifywHV88{*h9BwoXWm?op za(Ps!Wnz-*bUF?7(e{4!{>O2aTb<96{-hEio%T{w;Ep4i)$ddGNa|0G@7 zO4!9bGoc(ND8l?2OnZ`|9cPt7VHanx(>NsUI zyB7}!+Ep2IL55l-X-_==le3bE!A7Bj5B0dU5Pp=ANa;~^H!%aH1I|-urA?nmNHxPu zp1}i@-OejKs2YvR>f^@C*3AU+Bw)XBp1Pvp5QpXo46qkSHq3(Yy%<06h?+w1ZXG5X ztg3oUS|sDtoiywySYI2-*tx|}<8A%8a>y+A^*XPvPcSF1gj>2G+J)FpYEL)LYMk{hK`cEb7# z_0Z{S$=B(4k7XY1X^-a<{PdQ^XR18~LDDiLB+;61m%UP>?WHfiQEFcBXG>pgosFBZ zoRTt<9TDr`SWw5wafX5mLFJ*zWqBPV*9-rFFr*Z={EB&uC;@Ud34~V0CyGSG)m{uP zD{!8MYBZ%aRNR;diHjFgv4Yo0^l2C_gAZky{8u^#)RR))O9-Ky2PqFz>oQwCjR`^4D>?1OPdkqNzTP&ey!@m~ zzbbs`Wo*N@c!R7>+(r3y-C-Mf&ZA>~oSb27U+|BDLl{2Q9d1>#8vaZUbJ1N%xhfc2 znzS3eh>@rn>+7Bwr&H57RLN6W&px-Oj3ZQY0!LO-+8qNEW)v%vx>G(~e+LLmE;@WS zB#O)ANRmkGbVNtLu#+pJ7(q9?P6I=tI1$n1t%ZLJDlN+?!p@9FmU(d&>hK?og8oNO zkm9-sypoY8*Klu?ainG2B?zuC|FvA6okZwxJ#f9Xw7qA$BM0Su2v1^ua+c9WvS62?#e4>@<}`jEm>O> z*-APgXRE|1n#Wj?vu-{I?O=9K_G9c{F1Bges>M~@JRE(S@e>yw9`l7(@xeAVKQI2X zSeDlK`#pp1SJ~#P<4#(RWA325qBhizuztB>|KecE9#+ikbsUrSh~K~{I5yB-W%*3; zd~vmLEn)JDvTsZ3w-flNdt~d!7G4Od$)=p}W1`tDhoZ=PEX*JytOb=U4svpnUZ1hn zicd60#nFaHFSlkh7p|TFO1C*tFM-)tgMIOM%1BC2R8>+fDgQzJvt&?`DSdXSQE}I- zAU4a?UI8P+gUViYaDHWa?acr(VgNdrbrgbountKL^rm^dE(h3vDUX7e*(JC$56VE& zEkZOfo&DtYJs`$Lh*FK`1B51$6=*fHfBQoF=I7^9y4Po*e6^ODh^epANOAZvO1*7H zaW}N5itgx{(RI&u62B=I}@=0 zCCJ`|mTp%*x;1`xn`L^yba<+)b64kzV6Qqjz0JrqS-s-QQ{T)aeCpslTK=^VfH;5@ zIbrhLTjZ`8rv*4+o=1=`@7Uv>Rhn@cSu(}yBfXIrCu()afu+c#Y;~fWh zJ3cf3FtIe|xD~PxH@C(LbRopGvNnKf2}RTn7cd$k*T`Qa=^Q+L4W5bw$uu{Kz*knD zUAzV1-6GOKYq}K9t5*TYY3__GSH{6pZUBPvTJls3xrCgaatboZDa0$=Ei)rcN^Z%74N)p@ESAP>YFiCn%C;7=cn^l`WDEA*DrNq%&BpcrXn@Zo_N`lk$KZ zydpGO1Um^$FQeAuzh;zC=7fuQUUK4~Aq`&o#| z5jR1HCcjHU4nB_10a`X|q2ui12TGxjsNp=s$tm9e_DuSpO_W&m47(O&FX)Ql(}y-C z0yd|M?QT;iPXR&23?Cnu>LF7=1gNg$o8C6!V3sq1mdm2JB#YIcX(DrwI-I5m3TUGE z|3?#b-L_9W6~aI!T&p!W_`KJI%=Td2UBCc~vMw?^eP~g@o+$oj_C!YpCQ9{xG*K^m zfJLFn1ln*3`PGO0AG(2g0At)>yT9vLrgCUed`Br|>E>m%l7OnDQigf|QC0hap-=$@ z2r1X<96as14c@UDFCF%3$2}^jwK^OYGnk@+>-Bw1kRyn4`gHI#l@|;>RMDWZGok(C zMd$rPqkarpyV~x8zY6p_M@Gm!fJjCJgQ;Ix_Bnsi6gfTVuq|@vEBL`H)V5qdAsw<= zkK8=4pnC?uo|;m;X7Av{Wl~104#X191onTa!EEdpkc3CMUi;vw79{M|@#$fScHEPK z`tbv?3?a`Wf-SF!1(IBha_c`7mIpiyRD9d0nb1C}8NPe_z=b7)mFvq=nrRvinkUom zB|mI77BqX(YB*zG0IWp8ze`OX*!iGJmYL~4UgKcAe5^pAO9~E3oS?Vl;|JfTDxZMX z9%cWdf4oN2;m82+H7nutd~7zl^DmCLh#Y**RAgaeK$T@aClbnaDAz}5rK~*IH3D|? zcm19Ot9*_R^Z3y}OYv}6&|uFL)bJkIP2}Gdd;`dGGRnd99~)OaFau+tZcKZ|S}U-d z!$m1*NCrNqGAsuXeh+eur8((vYj6bLKMeS|_aA;u1U;lF#u8K0NaZ)UWr+L$P)_)# zfb7w#$s57~0Vv!zCSqd&Xi3V~8G`1BHY}-T0jLbkh3BK5Z=%cV$K;XCJHIo-@4ya3 z=Zo+KS{f0W*)$dCO~(cUSWGK`R`5V9m!&sWd#FT!rj2!9C8Yq+A)3F2A^cBuk}i_e zMoE%=Cp#4d3c1oyS$6^;Tj&7{f}sTn&U*Yi0G+T)j|Z=0nddoIb+-U22piy!C0V&IA!UCPBZ>;|Yya)ez0rw*|q9M38?t z3z$c=mK%Wg6A0l$rV6`DTmi6a0w9VQAclqPN{<9y!v{i0L1-n}&2Jg406W484zM3r z0%nay?oS3EK>oOL07?nlnc{5)Bp_}@0Jb_}@GJZiAc-(S*bRSuqit3mgPc=fN%92& z4bH_Xdmgc1A`;KlVId4u%cI9nEGX}Z(}fJ#+>XAREPrMcAO|3FgdccN2|#pV+Q=u( zds`E60?CvY78|^F_AjoFG9Ulcu=0+p`j+nbXWwqOA%?mE5bVOvV%R>h1x9+-gL)GM zbfpMpT+#Xloiyc-!m!W-x{Zm!Mb`F7*BA5f0s#EUg5`U3a7-;j*f80NDCQPWl4oiE z261u#!i(rW;!@xK@DHmQmY-Uee1_Yr2j=_5=ks9f0oo6O&9rckWWV1ui8))*fNI!X z8!?JX_s9p3zPk?^0IXzE9ALphSV~S8_~T@M-piPL1|fT{I~M-j0nj+1gxAGu8C=o6 zT3`VwtAbTQCD;E%o$OqEJ9M;c3CJMU;6R-7K30$`E>+FF5%8bHgW|&R)Gyp@mjMni zu9*5x47(bou{6+`@Qn1Z{arwx+VaZfeMV;r$-idmTVQ2V~Dsk zU&hp%Xt=px9op>ovmXV-`Bd?Hdd@=-t;T~O-7OE%#f)msSH2`$ZkQJFcILm)Wr3lN zTu;;FiO=o;SR@$?-~-yXuW3-)o^}ty@I1gNa zPsm-Ha+p0p>g=ituf9d5M{@z-72dZj&3p%-&?cXEKtM9v<+0$Ew;RT~VYsx1)+LE< zpr#}HT~|823c%phBL)hw>g?$gcL4=S-s5@JcvemceaVe({V_n05oklqr=stPwZHWs zo_}u7QeATctKPO8R?^0GJS`Z^STyh=l57w;|2@rcs95<;rT^eV3xiVIdgpx1WtvJlZ8BK z()8A*AYeM)qzt%^WlB=qgL|nSc)hXrVS6iCnX@;s>fGsifGR_vvCFcK5jHf}-GIO$ z(p=f?uK*usM(1of%fMB?FIbuW7KoM9vvv_8xj5#P3H5go=m9=)S$GF)_jIo6O@~)9 zV7_I=?{pi4GC?$~Eb)B6izLYhDr6%f4>zr=VCDVV z(s*NW;0poxY;vTx?BSFWwMk7`qgtH!_t7pSf?m!Scu58WrCslyuMn_uStz-wzqgzK z)xHQ!-R!?TY3eGQm?C1n3iQjcleFNeKV7OK0bbIz=Nj%pOL7ymnELjtTVvYCyHpYI z>@C|}{=_c&y#>E!nu?|^M@wdHv)=$enK;Xvovy+D`vXm9MjGK)UKx_Z)OoQYWWd_u zaGQS))ck2R=~k^qkq}ACB#vWj)TP0)3gG$HUc5)l9x*T702Gtc5aG|rJxyv#VGQmX z%Jtj?FkWT=Pwu>};!sX(JqZ>cCvz>BBN6a=SlT$V)z;F!eZK{KvZ8*q6`&k0x5G<5 zc3)4ILgxWtsg(eJ5+BxGfJlVMUa}Y8{gLYAtAtg4jCChm6q>wwq_vPs^d(E}c+TUC zheV9$AebGbdtKM3M|O_b?#gL?yN)rEzyM2y9~gfZIMf$6Xf7RfYR8>ue0;FMrh~27 z&;(3Jj=X*~4YZNV%0g(}4Kqo%3s(*|eFN~b!ft_Q1uICFJ~ti2xXPcwyDySQDRe>T ziR7sKbhwvZ1BwdRybwEf1|&;WAvX`=jwetQPghIl1EOq_ZjsAjkDi5+rTWDKd+YT8BS;pgZQ)^NpC_)xt`Ab=9)uo;@#+KN2w`}$FsqcoKctsc z#C2QJwY?9+5maIYti)udgSP(@=>!5X2ff(r596A98bG`p&6R}|oVeTmNYJ5p5Fv*A zf!UA*=)2%fz4f7ZAhHuTeLp=U+Kwv-t^a(u9H59W-o5-425vc zJ(CY4@otko440pQ$(WvAuGxN!#f&ts=^q43fy!V!$7ZC>IYFNe_T|`vr=B1I?)kLh zuy%!%Rbn-tc`z4$QbB96OD@l+fFvh19itD0y#PL35a27{I}7jsUD|k<^iV@d&r7PD zUU5kOcWMxaYJ9m5WRPe2zm+0D*}r4q;lh1P?)r2hrgFf-DOw;!0p(oNry+37eG>q( z*z3kax5YrAf9Wpo?t{nDLs{hVXrKj=^nt^~*qj-n-L^s$S17Pq0ufg9!;+jc5DCM;3%G z`$$Vg3+Cm2BXIYMMl5%5i3S5ks=%HL0P1r5T*PH4pM+YUk#ae#P3SQ3y?FXwal7s3 z4+xZQ%jyS00-fPUMojETK=r<^9dNi}w5~Ifa}J@a6K;fm=dXp5zvx^^R1IG9Iz#(xc5?0DnPlH3>*eL11Ti)(`?%Z45a_Hu#w8*{HW1woS+ z8Mzh6(haT10Hv>@Vp*-mk|e8so##AQO8|+q!)rvC_A+TO*TeQPh$g1V zJzoqF7F=HxvPXwXc&$T`sD%xaa*UuF=5BI5-yU^r$-&A&$ z$xUvlMp6CAI{1mydF&nFR7NOJeeSJu# zDC0*ypr{jM&%QW&E3ppsFOegOB%smxkFvN zOFHQUIR!C$=qC!gj$=ZG#}0quq}9;_9{B^1^X;jN48wQx4*U1wnP0mKbi@Ja=;AS# zRwm(yLxo4F5Hg|lt$_ypzkT4pvB>m*`A`A*8l`ohh}O*rsV{MAE{?nj%q1AV_uzrT zj~+Cp?3@8ns-TG_)!ZSpLyaXs)8~QI0`;MT*}JJx0ToFcfyLzNxp3dT!^93|3mC~H zA4PO5l#k9x9?agfT?o^R&|&wYnz=5ZpCO^35Yyq^n6xl#Jw4jiRVg_DxehCcW0^qENQGs0TO`OC& zz`LWDWr1@|YeR5oR3#G_0X?Iq@{`$>XaKC0QCNC;%o-$$Db2k9M{;O(E#NzZi;Y#I zmlK2v!`z`86V`xyz}x*Ay5M2J2e6Ym$LpbV=;hH9ZA#<5BQDm^r2{@8``hCTr}NxZ zvp_0!><8bUcYw|yOz8*sG?m~U2=yQ<5LFNE0ggJGLwgeLNL_s*@x|aaoL|sJ= z5JqBbZkeC51c|ZH3}AJmjWE(|p=y{hAnXLV+rh8L9iebKn~?6m2l6Klr~skd2t~%= zLV~j`+Nbkb-hti59T0n>K^7smw|%aDlxYEUSP3XBookG@Ax=I^VVu7DSL(B6jK}9d zy2hR(L4>$sExYUEyntpy5(E33_pvZDmjka<%{k_428u>bRqbXs%(e5F7Wiz<8(EV(U^JW*#M5jJ3&Qu>?Xl&jJx zdzx@*KOIoSxK@Y>PzI&^m96wN%>Z#dCHea%*dUqc+XT$jq82Q@-b#IFPFoEI#xm(G zYxD5_t%P|!u=5Kf=i25t6f2t{eepL%;4d_XZb5qn0g4_skA~0S-y()yfl`c*`Z2~zP61eS3s?_qp?~i3w$q-P4_*u!r-2e& zOWpu*!;6M^*G&o0vdU{|VfertDPoy{;K&dXPP$$89Rg@{fTEEwPq*g+sFxTaAv>M` zxBw5n+%0f>OLR2cqDzV>MB8)_Nl@MBIs?VVi3yM^Hd>zf*er1z1783uD=}NW#6E19 zXr+FqYxosFrCe#nTv;im9Fni=;a};z>9sP_3z!y*&HJHwfwuFANLO&p3=MEqm|ztO z6|LNci9~mW)sH54ie+%83f+sjDqWU}M7lkwvZ5K3+m6F0rS!v{G`-ajgh&w|p^a4gOW~TR=;1!r z&{5wAJ~^Wb?WE{RrxR56ha-M7tgHN)fERoq|fP|lDk6+xs9w`+X(s+gscmoX!g}z zqTaq-#U_}WUy3zu3f*40jT3J-at6V7#A{})Rohm(AA{JOawl-f$FmI|7Daq#owzei z|Ks1Q6=4@c3)}$Jji;MD>Rhh%igqJQ+vMrOV5Sam-^K^=lt=j=n@?r@TD>Ca4$jN0 zj1SV6uW!hW8;6#Jn5qb%uJbcae|ia00*iiok$oh2U4s#SMt-WL#qy#gLRETIOA!hIBzt6G=(wtD#_ zm;cI;(%z_L7jg`+E+ZP@oAPf!0f|lN(>lblU9w?Y3l4@ZH-cceO8c7IfKZ8`s1fho zthDxWRosrf?slHiZ!1DCxL^sqlEg7yyd`_v4GZ{&W6Ru4H$pxdX-1b8hRSahJ~?XR;}*mW2;@qo>Tgged@bgKZfiI zlgQ#1Wd*V}s$N4pL8#U%4Bu2AgSM1tK}3?9$O@X3oWTlxN36DcJQqz__-=|h%-KKb z={?0J$KQamb&lFV{Y+tdYt!@xnQB?EQq+y&453SJT0!-Zy}4L*=(yOFm?{4M@Bt; z;~FZIV0-&xR8*xRXKvM|P+a?<>9SRbROpQyz4q=loW$e?AoQsa#_VIAong!p#9Dd- zE7VvI&SI}d283UFl=p<;Fk8xJ;KHbW!A$I`s2cQyp_E5 zR!+-jt&JU?NxD0&mkk($38I^VJXQn;mNv%1)hVabNs(9(sr65mu27tXcdH{Vfu*rC zxCYhAKZ`-hrS&ay+xQ_(>TJ!e4>Ckecv{a%#7)((uT`z|7ut}g*H);Msk8Ne@y!kb z1Rr)T~Fl^dzD<#umdp!t!~~C6SBI*i`>l76Y$b4Sp%2ZB&ogt zJf|#SxD!(xg+mF>Beh5YI^$J7O$7}wI1Z_e|BATGGX6@Q?wy9yS0GBHp}|nR5s7FSJ+z|Nr?i}$l$Fgnjty2 za6w=6*|&6Ao8+1AFZ$x#+Hgi8gp06Uz%iZIG8Ml%Hu_E(!$QnYgf~{`k_Ll#?IXwq zQ~4uU+7Yhbf;!8%2szb8Sgx)usgfeVf0^8``KZ+AV9_20)U=s%tK@9ar7y# z&gU{!E^%Vyu>K-Uyfj;tb(x+zMw&V@b~QEbJR8f{{K}<{~FR$$d|V zG`J(}t%gLEzug41!k)OPfb9U_XA)ZgTxzW|yf*h({`>j|+d;(KM7}pc?wtc? zJ|4rFJ}%H+U|AZQC2+$ZU}}#KoJem}6{F{pMkc_brobgTtk9iHvfsOJUBDC~B(wE- z6odWqlBef?O-DYp&S5u-UYb0f1eNtk7su#%v<~+`5Qpx480iIfH_1F>0Q6e-`P$DN z4=tmZX4i8h?hW?)&E|EC!FO1eN;N7=rzN%-EyHd!Ym zxP`1$_9C#hNI1CmV|zZLzRMv9#E(g$B7h>s$lQjo>?V|8jT=LW!S}Hk50(*d=3Mlf z+_-6I$(XXggh1Af*gRwUWiN%|@EX(B)>F8`y^_2mqJ=AE`?4&CE)sSQz=e*F7= zhF_ZGGPeN`w2tw`46`pPIw|~{P(hQM8k;!#%npJS=TcTJm_aOx;oa6lXc2!+aVJmQ z=N^mEko~O+bJEKl;fSL`6iSsgL?@@1oE5QZ-rdw8Wero-DS;()4vG>v5=|J1gqK@S zW}Wh0fD?^QnfUIT|ErU84~j7g!#Jx!qqHrB5{um~6X_yKYwSpAH`cO>kzvQUZMD%1 zrb4VsWpq!`WmuPHGH%nTWnxW679%q#OuKHmi$zk*FvIhn!}{NU&3E?u-uJxU+4g;Y z=b2NxH1?YKe)hxDE7EZ3fgPMk}-4oK5k z&-#%WmfA$1+DGtP&dvF*iinoceZ22_<%A7|k^!bWDJu3x8rLar@ll zBlwPssdsU1x)ZdTE7XJC+Vk&G`cJGu_t7O)$lq{j88tKK)B^ltYxbwEW|UOuRxB^T zr@%*2rbL7%2t;wgOqO@(X)k_i{=-sLz7s&vx5V zFNPO`JxJqaCWA|zD`nM~o}Z%xCS%xO*T~^x$SGlLW4&+H;a-LD03R}s)|)1w#_?1z zQ=#dFf+HB;$b38Y@YS1GjX%2C%W%pU3zHibi_u~W$q%-$sX8e$G0Sd}iR7opM@?V}*ccOQDuK{l)ne3H?fO8RILEzUNPWPYhU zhL=BZx-cA@2yxYPPlH+aiC_p4Yt`>2Q-V>%5)(AV#u=k|6d$h$W+Br0(~!%ag6m3S z7avL(TVjP5M53UQm8u#vglTA5Mzz~7fWXAlRI+YbP0~9${>D^A;`d};2_8&C0}U>~ zEP-OV3Vbs%({`CTz8EbZhu!_5?gtqd#51sfYlxqJF>RxMn@F5o5Dj=fdVH|kziom@ z0_QLFKe+RYHPi4&{6(J>3Iqv;q1KzWKCD*u;Qr1oFzlq~HOrPcuKaO_{n_w9D8Ndf zTcIHnrQyLvH^`6Vx8AmxV_7Q9Y#`Wif}RbIE5*Zr+*MCZXa9zZ)8=8S(N3>B9aqGD zxU1%peubkP%|fV-|0z(ua9`vm>%S^uYKNMC4cMA~MG)pRs13(d_fy2EW}lg*-T4~` Cw>Urm literal 0 HcmV?d00001 diff --git a/docs/modules/ROOT/partials/nav.adoc b/docs/modules/ROOT/partials/nav.adoc index 6b8693089..73fdfd24a 100644 --- a/docs/modules/ROOT/partials/nav.adoc +++ b/docs/modules/ROOT/partials/nav.adoc @@ -1,5 +1,8 @@ * xref:index.adoc[Home] +.Explanations +* xref:explanations/crossplane-secrets-non-provider.adoc[Manage secrets not coming from a provider] + .Tutorials * xref:tutorials/install-cloudscale.adoc[Install Cloudscale Stack] * xref:tutorials/install-exoscale.adoc[Install Exoscale Stack]