Skip to content

Commit e127558

Browse files
author
Jim Ryan
authored
Add mgmt configmap tests (#6957)
* test full mgmt config params * fix mgmt client auth runtime secret location * add test for full suit of mgmt configmap params * update snaps, fix signature in tests * update snaps
1 parent 6ce3e72 commit e127558

File tree

7 files changed

+230
-43
lines changed

7 files changed

+230
-43
lines changed

internal/configs/configurator.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -953,6 +953,12 @@ func (cnf *Configurator) AddOrUpdateSpecialTLSSecrets(secret *api_v1.Secret, sec
953953
}
954954
}
955955

956+
// AddOrUpdateMGMTClientAuthSecret adds or updates the MGMT Client Auth Secret file with a TLS cert and key.
957+
func (cnf *Configurator) AddOrUpdateMGMTClientAuthSecret(secret *api_v1.Secret) {
958+
data := GenerateCertAndKeyFileContent(secret)
959+
cnf.nginxManager.CreateSecret("mgmt/client", data, nginx.ReadWriteOnlyFileMode)
960+
}
961+
956962
// GenerateCertAndKeyFileContent generates a pem file content from the TLS secret.
957963
func GenerateCertAndKeyFileContent(secret *api_v1.Secret) []byte {
958964
var res bytes.Buffer
@@ -1323,9 +1329,7 @@ func (cnf *Configurator) updateStreamServersInPlus(upstream string, servers []st
13231329
// UpdateConfig updates NGINX configuration parameters.
13241330
//
13251331
//gocyclo:ignore
1326-
func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, mgmtCfgParams *MGMTConfigParams, resources ExtendedResources) (Warnings, error) {
1327-
cnf.CfgParams = cfgParams
1328-
cnf.MgmtCfgParams = mgmtCfgParams
1332+
func (cnf *Configurator) UpdateConfig(resources ExtendedResources) (Warnings, error) {
13291333
allWarnings := newWarnings()
13301334
allWeightUpdates := []WeightUpdate{}
13311335

@@ -1334,12 +1338,12 @@ func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, mgmtCfgParams *MG
13341338
if err != nil {
13351339
return allWarnings, fmt.Errorf("error when updating dhparams: %w", err)
13361340
}
1337-
cfgParams.MainServerSSLDHParam = fileName
1341+
cnf.CfgParams.MainServerSSLDHParam = fileName
13381342
}
13391343

13401344
// Apply custom main-template defined in ConfigMap obj
1341-
if cfgParams.MainTemplate != nil {
1342-
err := cnf.templateExecutor.UpdateMainTemplate(cfgParams.MainTemplate)
1345+
if cnf.CfgParams.MainTemplate != nil {
1346+
err := cnf.templateExecutor.UpdateMainTemplate(cnf.CfgParams.MainTemplate)
13431347
if err != nil {
13441348
return allWarnings, fmt.Errorf("error when parsing the main template: %w", err)
13451349
}
@@ -1348,8 +1352,8 @@ func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, mgmtCfgParams *MG
13481352
cnf.templateExecutor.UseOriginalMainTemplate()
13491353
}
13501354

1351-
if cfgParams.IngressTemplate != nil {
1352-
err := cnf.templateExecutor.UpdateIngressTemplate(cfgParams.IngressTemplate)
1355+
if cnf.CfgParams.IngressTemplate != nil {
1356+
err := cnf.templateExecutor.UpdateIngressTemplate(cnf.CfgParams.IngressTemplate)
13531357
if err != nil {
13541358
return allWarnings, fmt.Errorf("error when parsing the ingress template: %w", err)
13551359
}
@@ -1358,8 +1362,8 @@ func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, mgmtCfgParams *MG
13581362
cnf.templateExecutor.UseOriginalIngressTemplate()
13591363
}
13601364

1361-
if cfgParams.VirtualServerTemplate != nil {
1362-
err := cnf.templateExecutorV2.UpdateVirtualServerTemplate(cfgParams.VirtualServerTemplate)
1365+
if cnf.CfgParams.VirtualServerTemplate != nil {
1366+
err := cnf.templateExecutorV2.UpdateVirtualServerTemplate(cnf.CfgParams.VirtualServerTemplate)
13631367
if err != nil {
13641368
return allWarnings, fmt.Errorf("error when parsing the VirtualServer template: %w", err)
13651369
}
@@ -1368,8 +1372,8 @@ func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, mgmtCfgParams *MG
13681372
cnf.templateExecutorV2.UseOriginalVStemplate()
13691373
}
13701374

1371-
if cfgParams.TransportServerTemplate != nil {
1372-
err := cnf.templateExecutorV2.UpdateTransportServerTemplate(cfgParams.TransportServerTemplate)
1375+
if cnf.CfgParams.TransportServerTemplate != nil {
1376+
err := cnf.templateExecutorV2.UpdateTransportServerTemplate(cnf.CfgParams.TransportServerTemplate)
13731377
if err != nil {
13741378
return allWarnings, fmt.Errorf("error when parsing the TransportServer template: %w", err)
13751379
}
@@ -1378,7 +1382,7 @@ func (cnf *Configurator) UpdateConfig(cfgParams *ConfigParams, mgmtCfgParams *MG
13781382
cnf.templateExecutorV2.UseOriginalTStemplate()
13791383
}
13801384

1381-
mainCfg := GenerateNginxMainConfig(cnf.staticCfgParams, cfgParams, mgmtCfgParams)
1385+
mainCfg := GenerateNginxMainConfig(cnf.staticCfgParams, cnf.CfgParams, cnf.MgmtCfgParams)
13821386
mainCfgContent, err := cnf.templateExecutor.ExecuteMainConfigTemplate(mainCfg)
13831387
if err != nil {
13841388
return allWarnings, fmt.Errorf("error when writing main Config")

internal/configs/configurator_test.go

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ func TestConfiguratorUpdatesConfigWithNilCustomMainTemplate(t *testing.T) {
101101
t.Parallel()
102102

103103
cnf := createTestConfigurator(t)
104-
warnings, err := cnf.UpdateConfig(&ConfigParams{MainTemplate: nil}, &MGMTConfigParams{}, ExtendedResources{})
104+
cnf.CfgParams = &ConfigParams{MainTemplate: nil}
105+
cnf.MgmtCfgParams = &MGMTConfigParams{}
106+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
105107
if err != nil {
106108
t.Fatal(err)
107109
}
@@ -117,9 +119,9 @@ func TestConfiguratorUpdatesConfigWithCustomMainTemplate(t *testing.T) {
117119
t.Parallel()
118120

119121
cnf := createTestConfigurator(t)
120-
warnings, err := cnf.UpdateConfig(&ConfigParams{
121-
MainTemplate: &customTestMainTemplate,
122-
}, &MGMTConfigParams{}, ExtendedResources{})
122+
cnf.CfgParams = &ConfigParams{MainTemplate: &customTestMainTemplate}
123+
cnf.MgmtCfgParams = &MGMTConfigParams{}
124+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
123125
if err != nil {
124126
t.Fatal(err)
125127
}
@@ -139,7 +141,9 @@ func TestConfiguratorUpdatesConfigWithNilCustomIngressTemplate(t *testing.T) {
139141
t.Parallel()
140142

141143
cnf := createTestConfigurator(t)
142-
warnings, err := cnf.UpdateConfig(&ConfigParams{IngressTemplate: nil}, &MGMTConfigParams{}, ExtendedResources{})
144+
cnf.CfgParams = &ConfigParams{IngressTemplate: nil}
145+
cnf.MgmtCfgParams = &MGMTConfigParams{}
146+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
143147
if err != nil {
144148
t.Fatal(err)
145149
}
@@ -155,7 +159,9 @@ func TestConfiguratorUpdatesConfigWithCustomIngressTemplate(t *testing.T) {
155159
t.Parallel()
156160

157161
cnf := createTestConfigurator(t)
158-
warnings, err := cnf.UpdateConfig(&ConfigParams{IngressTemplate: &customTestIngressTemplate}, &MGMTConfigParams{}, ExtendedResources{})
162+
cnf.CfgParams = &ConfigParams{IngressTemplate: &customTestIngressTemplate}
163+
cnf.MgmtCfgParams = &MGMTConfigParams{}
164+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
159165
if err != nil {
160166
t.Fatal(err)
161167
}
@@ -175,9 +181,9 @@ func TestConfigratorUpdatesConfigWithCustomVStemplate(t *testing.T) {
175181
t.Parallel()
176182

177183
cnf := createTestConfigurator(t)
178-
warnings, err := cnf.UpdateConfig(&ConfigParams{
179-
VirtualServerTemplate: &customTestVStemplate,
180-
}, &MGMTConfigParams{}, ExtendedResources{})
184+
cnf.CfgParams = &ConfigParams{VirtualServerTemplate: &customTestVStemplate}
185+
cnf.MgmtCfgParams = &MGMTConfigParams{}
186+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
181187
if err != nil {
182188
t.Fatal(err)
183189
}
@@ -197,9 +203,9 @@ func TestConfiguratorUpdatesConfigWithNilCustomVSemplate(t *testing.T) {
197203
t.Parallel()
198204

199205
cnf := createTestConfigurator(t)
200-
warnings, err := cnf.UpdateConfig(&ConfigParams{
201-
VirtualServerTemplate: nil,
202-
}, &MGMTConfigParams{}, ExtendedResources{})
206+
cnf.CfgParams = &ConfigParams{VirtualServerTemplate: nil}
207+
cnf.MgmtCfgParams = &MGMTConfigParams{}
208+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
203209
if err != nil {
204210
t.Fatal(err)
205211
}
@@ -215,9 +221,11 @@ func TestConfigratorUpdatesConfigWithCustomTStemplate(t *testing.T) {
215221
t.Parallel()
216222

217223
cnf := createTestConfigurator(t)
218-
warnings, err := cnf.UpdateConfig(&ConfigParams{
224+
cnf.MgmtCfgParams = &MGMTConfigParams{}
225+
cnf.CfgParams = &ConfigParams{
219226
TransportServerTemplate: &customTestTStemplate,
220-
}, &MGMTConfigParams{}, ExtendedResources{})
227+
}
228+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
221229
if err != nil {
222230
t.Fatal(err)
223231
}
@@ -237,9 +245,9 @@ func TestConfiguratorUpdatesConfigWithNilCustomTStemplate(t *testing.T) {
237245
t.Parallel()
238246

239247
cnf := createTestConfigurator(t)
240-
warnings, err := cnf.UpdateConfig(&ConfigParams{
241-
TransportServerTemplate: nil,
242-
}, &MGMTConfigParams{}, ExtendedResources{})
248+
cnf.CfgParams = &ConfigParams{TransportServerTemplate: nil}
249+
cnf.MgmtCfgParams = &MGMTConfigParams{}
250+
warnings, err := cnf.UpdateConfig(ExtendedResources{})
243251
if err != nil {
244252
t.Fatal(err)
245253
}

internal/k8s/controller.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -893,7 +893,13 @@ func (lbc *LoadBalancerController) updateAllConfigs() {
893893
if mgmtErr != nil {
894894
nl.Errorf(lbc.Logger, "configmap %s/%s: %v", lbc.mgmtConfigMap.GetNamespace(), lbc.mgmtConfigMap.GetName(), mgmtErr)
895895
}
896-
// update special license secret in mgmtConfigParams
896+
}
897+
898+
lbc.configurator.CfgParams = cfgParams
899+
lbc.configurator.MgmtCfgParams = mgmtCfgParams
900+
901+
// update special license secret in mgmtConfigParams
902+
if lbc.mgmtConfigMap != nil && lbc.isNginxPlus {
897903
if mgmtCfgParams.Secrets.License != "" {
898904
secret, err := lbc.client.CoreV1().Secrets(lbc.mgmtConfigMap.GetNamespace()).Get(context.TODO(), mgmtCfgParams.Secrets.License, meta_v1.GetOptions{})
899905
if err != nil {
@@ -909,7 +915,7 @@ func (lbc *LoadBalancerController) updateAllConfigs() {
909915
nl.Errorf(lbc.Logger, "secret %s/%s: %v", lbc.mgmtConfigMap.GetNamespace(), mgmtCfgParams.Secrets.TrustedCert, err)
910916
}
911917
if _, hasCRL := secret.Data[configs.CACrlKey]; hasCRL {
912-
mgmtCfgParams.Secrets.TrustedCRL = secret.Name
918+
lbc.configurator.MgmtCfgParams.Secrets.TrustedCRL = secret.Name
913919
}
914920
lbc.specialSecrets.trustedCertSecret = fmt.Sprintf("%s/%s", secret.Namespace, secret.Name)
915921
lbc.handleSpecialSecretUpdate(secret, reloadNginx)
@@ -924,14 +930,11 @@ func (lbc *LoadBalancerController) updateAllConfigs() {
924930
lbc.handleSpecialSecretUpdate(secret, reloadNginx)
925931
}
926932
}
927-
928933
resources := lbc.configuration.GetResources()
929-
930934
nl.Debugf(lbc.Logger, "Updating %v resources", len(resources))
931-
932935
resourceExes := lbc.createExtendedResources(resources)
936+
warnings, updateErr := lbc.configurator.UpdateConfig(resourceExes)
933937

934-
warnings, updateErr := lbc.configurator.UpdateConfig(cfgParams, mgmtCfgParams, resourceExes)
935938
eventTitle := "Updated"
936939
eventType := api_v1.EventTypeNormal
937940
eventWarningMessage := ""
@@ -1874,7 +1877,7 @@ func (lbc *LoadBalancerController) handleSpecialSecretUpdate(secret *api_v1.Secr
18741877
return
18751878
}
18761879

1877-
if ok := lbc.writeSpecialSecrets(secret, secretNsName, specialTLSSecretsToUpdate); !ok {
1880+
if ok := lbc.writeSpecialSecrets(secret, specialTLSSecretsToUpdate); !ok {
18781881
// if not ok bail early
18791882
return
18801883
}
@@ -1910,7 +1913,12 @@ func (lbc *LoadBalancerController) handleSpecialSecretUpdate(secret *api_v1.Secr
19101913
}
19111914

19121915
// writeSpecialSecrets generates content and writes the secret to disk
1913-
func (lbc *LoadBalancerController) writeSpecialSecrets(secret *api_v1.Secret, secretNsName string, specialTLSSecretsToUpdate []string) bool {
1916+
func (lbc *LoadBalancerController) writeSpecialSecrets(secret *api_v1.Secret, specialTLSSecretsToUpdate []string) bool {
1917+
secretNsName := generateSecretNSName(secret)
1918+
var mgmtClientAuthNamespaceName string
1919+
if lbc.configurator.MgmtCfgParams != nil {
1920+
mgmtClientAuthNamespaceName = fmt.Sprintf("%s/%s", lbc.metadata.pod.Namespace, lbc.configurator.MgmtCfgParams.Secrets.ClientAuth)
1921+
}
19141922
switch secret.Type {
19151923
case secrets.SecretTypeLicense:
19161924
err := lbc.configurator.AddOrUpdateLicenseSecret(secret)
@@ -1922,7 +1930,12 @@ func (lbc *LoadBalancerController) writeSpecialSecrets(secret *api_v1.Secret, se
19221930
case secrets.SecretTypeCA:
19231931
lbc.configurator.AddOrUpdateCASecret(secret, fmt.Sprintf("mgmt/%s", configs.CACrtKey), fmt.Sprintf("mgmt/%s", configs.CACrlKey))
19241932
case api_v1.SecretTypeTLS:
1925-
lbc.configurator.AddOrUpdateSpecialTLSSecrets(secret, specialTLSSecretsToUpdate)
1933+
// if the secret name matches the specified
1934+
if secretNsName == mgmtClientAuthNamespaceName {
1935+
lbc.configurator.AddOrUpdateMGMTClientAuthSecret(secret)
1936+
} else {
1937+
lbc.configurator.AddOrUpdateSpecialTLSSecrets(secret, specialTLSSecretsToUpdate)
1938+
}
19261939
}
19271940
return true
19281941
}
@@ -1961,7 +1974,7 @@ func (lbc *LoadBalancerController) specialSecretValidation(secretNsName string,
19611974
}
19621975
}
19631976
if secretNsName == lbc.specialSecrets.clientAuthSecret {
1964-
err := lbc.validationTLSSpecialSecret(secret, configs.ClientAuthCertSecretFileName, specialTLSSecretsToUpdate)
1977+
err := secrets.ValidateTLSSecret(secret)
19651978
if err != nil {
19661979
nl.Errorf(lbc.Logger, "Couldn't validate the special Secret %v: %v", secretNsName, err)
19671980
lbc.recorder.Eventf(lbc.metadata.pod, api_v1.EventTypeWarning, "Rejected", "the special Secret %v was rejected, using the previous version: %v", secretNsName, err)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: nginx-config-mgmt
5+
namespace: nginx-ingress
6+
data:
7+
license-token-secret-name: "license-token"
8+
ssl-trusted-certificate-secret-name: "ssl-trusted-cert"
9+
ssl-certificate-secret-name: "ssl-cert"
10+
ssl-verify: "false"
11+
enforce-initial-report: "false"
12+
usage-report-endpoint: "product.connect.nginx.com"
13+
usage-report-interval: "2h"
14+
resolver-addresses: "1.1.1.1,8.8.8.8"
15+
resolver-ipv6: "false"
16+
resolver-valid: "1h"
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: ssl-cert
5+
type: kubernetes.io/tls
6+
data:
7+
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURMakNDQWhZQ0NRREFPRjl0THNhWFdqQU5CZ2txaGtpRzl3MEJBUXNGQURCYU1Rc3dDUVlEVlFRR0V3SlYKVXpFTE1Ba0dBMVVFQ0F3Q1EwRXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MApaREViTUJrR0ExVUVBd3dTWTJGbVpTNWxlR0Z0Y0d4bExtTnZiU0FnTUI0WERURTRNRGt4TWpFMk1UVXpOVm9YCkRUSXpNRGt4TVRFMk1UVXpOVm93V0RFTE1Ba0dBMVVFQmhNQ1ZWTXhDekFKQmdOVkJBZ01Ba05CTVNFd0h3WUQKVlFRS0RCaEpiblJsY201bGRDQlhhV1JuYVhSeklGQjBlU0JNZEdReEdUQVhCZ05WQkFNTUVHTmhabVV1WlhoaApiWEJzWlM1amIyMHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcDZLbjdzeTgxCnAwanVKL2N5ayt2Q0FtbHNmanRGTTJtdVpOSzBLdGVjcUcyZmpXUWI1NXhRMVlGQTJYT1N3SEFZdlNkd0kyaloKcnVXOHFYWENMMnJiNENaQ0Z4d3BWRUNyY3hkam0zdGVWaVJYVnNZSW1tSkhQUFN5UWdwaW9iczl4N0RsTGM2SQpCQTBaalVPeWwwUHFHOVNKZXhNVjczV0lJYTVyRFZTRjJyNGtTa2JBajREY2o3TFhlRmxWWEgySTVYd1hDcHRDCm42N0pDZzQyZitrOHdnemNSVnA4WFprWldaVmp3cTlSVUtEWG1GQjJZeU4xWEVXZFowZXdSdUtZVUpsc202OTIKc2tPcktRajB2a29QbjQxRUUvK1RhVkVwcUxUUm9VWTNyemc3RGtkemZkQml6Rk8yZHNQTkZ4MkNXMGpYa05MdgpLbzI1Q1pyT2hYQUhBZ01CQUFFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFLSEZDY3lPalp2b0hzd1VCTWRMClJkSEliMzgzcFdGeW5acS9MdVVvdnNWQTU4QjBDZzdCRWZ5NXZXVlZycTVSSWt2NGxaODFOMjl4MjFkMUpINnIKalNuUXgrRFhDTy9USkVWNWxTQ1VwSUd6RVVZYVVQZ1J5anNNL05VZENKOHVIVmhaSitTNkZBK0NuT0Q5cm4yaQpaQmVQQ0k1ckh3RVh3bm5sOHl3aWozdnZRNXpISXV5QmdsV3IvUXl1aTlmalBwd1dVdlVtNG52NVNNRzl6Q1Y3ClBwdXd2dWF0cWpPMTIwOEJqZkUvY1pISWc4SHc5bXZXOXg5QytJUU1JTURFN2IvZzZPY0s3TEdUTHdsRnh2QTgKN1dqRWVxdW5heUlwaE1oS1JYVmYxTjM0OWVOOThFejM4Zk9USFRQYmRKakZBL1BjQytHeW1lK2lHdDVPUWRGaAp5UkU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
8+
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFb3dJQkFBS0NBUUVBcWVpcCs3TXZOYWRJN2lmM01wUHJ3Z0pwYkg0N1JUTnBybVRTdENyWG5LaHRuNDFrCkcrZWNVTldCUU5semtzQndHTDBuY0NObzJhN2x2S2wxd2k5cTIrQW1RaGNjS1ZSQXEzTVhZNXQ3WGxZa1YxYkcKQ0pwaVJ6ejBza0lLWXFHN1BjZXc1UzNPaUFRTkdZMURzcGRENmh2VWlYc1RGZTkxaUNHdWF3MVVoZHErSkVwRwp3SStBM0kreTEzaFpWVng5aU9WOEZ3cWJRcCt1eVFvT05uL3BQTUlNM0VWYWZGMlpHVm1WWThLdlVWQ2cxNWhRCmRtTWpkVnhGbldkSHNFYmltRkNaYkp1dmRySkRxeWtJOUw1S0Q1K05SQlAvazJsUkthaTAwYUZHTjY4NE93NUgKYzMzUVlzeFR0bmJEelJjZGdsdEkxNURTN3lxTnVRbWF6b1Z3QndJREFRQUJBb0lCQVFDUFNkU1luUXRTUHlxbApGZlZGcFRPc29PWVJoZjhzSStpYkZ4SU91UmF1V2VoaEp4ZG01Uk9ScEF6bUNMeUw1VmhqdEptZTIyM2dMcncyCk45OUVqVUtiL1ZPbVp1RHNCYzZvQ0Y2UU5SNThkejhjbk9SVGV3Y290c0pSMXBuMWhobG5SNUhxSkpCSmFzazEKWkVuVVFmY1hackw5NGxvOUpIM0UrVXFqbzFGRnM4eHhFOHdvUEJxalpzVjdwUlVaZ0MzTGh4bndMU0V4eUZvNApjeGI5U09HNU9tQUpvelN0Rm9RMkdKT2VzOHJKNXFmZHZ5dGdnOXhiTGFRTC94MGtwUTYyQm9GTUJEZHFPZVBXCktmUDV6WjYvMDcvdnBqNDh5QTFRMzJQem9idWJzQkxkM0tjbjMyamZtMUU3cHJ0V2wrSmVPRmlPem5CUUZKYk4KNHFQVlJ6NWhBb0dCQU50V3l4aE5DU0x1NFArWGdLeWNrbGpKNkY1NjY4Zk5qNUN6Z0ZScUowOXpuMFRsc05ybwpGVExaY3hEcW5SM0hQWU00MkpFUmgySi9xREZaeW5SUW8zY2czb2VpdlVkQlZHWTgrRkkxVzBxZHViL0w5K3l1CmVkT1pUUTVYbUdHcDZyNmpleHltY0ppbS9Pc0IzWm5ZT3BPcmxEN1NQbUJ2ek5MazRNRjZneGJYQW9HQkFNWk8KMHA2SGJCbWNQMHRqRlhmY0tFNzdJbUxtMHNBRzR1SG9VeDBlUGovMnFyblRuT0JCTkU0TXZnRHVUSnp5K2NhVQprOFJxbWRIQ2JIelRlNmZ6WXEvOWl0OHNaNzdLVk4xcWtiSWN1YytSVHhBOW5OaDFUanNSbmU3NFowajFGQ0xrCmhIY3FIMHJpN1BZU0tIVEU4RnZGQ3haWWRidUI4NENtWmlodnhicFJBb0dBSWJqcWFNWVBUWXVrbENkYTVTNzkKWVNGSjFKelplMUtqYS8vdER3MXpGY2dWQ0thMzFqQXdjaXowZi9sU1JxM0hTMUdHR21lemhQVlRpcUxmZVpxYwpSMGlLYmhnYk9jVlZrSkozSzB5QXlLd1BUdW14S0haNnpJbVpTMGMwYW0rUlk5WUdxNVQ3WXJ6cHpjZnZwaU9VCmZmZTNSeUZUN2NmQ21mb09oREN0enVrQ2dZQjMwb0xDMVJMRk9ycW40M3ZDUzUxemM1em9ZNDR1QnpzcHd3WU4KVHd2UC9FeFdNZjNWSnJEakJDSCtULzZzeXNlUGJKRUltbHpNK0l3eXRGcEFOZmlJWEV0LzQ4WGY2ME54OGdXTQp1SHl4Wlp4L05LdER3MFY4dlgxUE9ucTJBNWVpS2ErOGpSQVJZS0pMWU5kZkR1d29seHZHNmJaaGtQaS80RXRUCjNZMThzUUtCZ0h0S2JrKzdsTkpWZXN3WEU1Y1VHNkVEVXNEZS8yVWE3ZlhwN0ZjanFCRW9hcDFMU3crNlRYcDAKWmdybUtFOEFSek00NytFSkhVdmlpcS9udXBFMTVnMGtKVzNzeWhwVTl6WkxPN2x0QjBLSWtPOVpSY21Vam84UQpjcExsSE1BcWJMSjhXWUdKQ2toaVd4eWFsNmhZVHlXWTRjVmtDMHh0VGwvaFVFOUllTktvCi0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

0 commit comments

Comments
 (0)